Stormworks: Build and Rescue

Stormworks: Build and Rescue

Not enough ratings
Pipe Theory - Functional XML
By Kernle Frizzle
To pipe, or not to pipe
that is the question

With the power of XML, you can say goodbye to space-filling lines of pipes. With Pipe Theory™, all pipe connections can be reduced to lengths and transitions; any two components placed anywhere in the editor with any rotation can be connected with two well-transformed pipes, and the best part is, it's fully systematic.

This guide will outline the context and process for building your own Pipe Theory™ abominations.
2
   
Award
Favorite
Favorited
Unfavorite
XML context and basics
This is going to be a repeat of the information you can find in the countless other XML guides, including this one.

Step by step:
  • Identify the part you want to edit
  • Place the part on your vehicle, then place a regular block next to it
  • Using the selection grid, select the part and the block, cut and then save as a vehicle
  • Navigate to AppData\Roaming\Stormworks\data\vehicles, find and open the XML save file for the new "vehicle" containing only the part and the block
  • Find the section in the XML that pertains to the part, usually indicated by the part name appearing in quotations
  • Find the transform, which in the XML format looks like r="1,0,0,0,1,0,0,0,1". If the part doesn't seem to have its own transform, go back to the game editor, rotate the part in any direction, save the part and block again and re-open the XML. To save on file size, there's a default part orientation that doesn't require a transform to be present in the save.
  • Edit the transform. We'll talk more about this in a second.
  • Save the XML file
  • In the game editor, load the vehicle containing the window and block with the selection grid and paste it on your vehicle
  • Use the block to merge the part to the correct subgrid.
There are times where the part can still be merged on its own after editing the transform, but including a block is a good backup (especially for windows).

What does the transform mean?
In 3D geometry, and especially computer graphics and programming, a transform is a way to represent an orientation in space in the form of, in this case, a 3x3 matrix of values.

Don't worry about "matrix," it's just a fancy word to make you scared

Orientation tangent - representing orientation with vectors
When you want to represent an orientation in space, you want to in essence redefine what the x, y and z axes mean for that orientation. Take for example, a radar placed on a vehicle that returns the x, y and z coordinates of a target in its view. The radar itself has no idea how the vehicle is positioned, only what the local x, y and z coordinates of the target are (if the radar was facing straight up, a target directly overhead would look like it was directly in front).

If we wanted to know what the actual map x, y and z coordinates of that target were, we would need to convert between the local space that the radar measures in and the global space that the radar's vehicle exists in. Here's where vectors come into play.

The conversion from local to global is easy: Each local axis of the radar's vehicle is defined as a vector in terms of the global space around it. You can imagine this quite literally as three arrows pointing in the directions of the local x, y and z axes of the radar. If we have the local coordinates of a target in the radar's view, we can "extend" those coordinates out into the global space by multiplying the x vector by the x component of the target's coordinates, y vector by the y component and z vector by the z component, then adding the results together to get the global position of the target.

The easiest way to store the information of those three vectors is by putting their components in a 3x3 matrix. The difference between column-major and row-major is beyond the scope of this guide, but some programming languages and graphics libraries prefer one over the other.

Back to XML
When you place a part in the in-game editor, you can rotate it however you want (and mirror it, but don't worry about that; the game has a separate way to represent it). The vehicle's XML save file then needs a way to represent that orientation, and thats where the r="1,0,0,0,1,0,0,0,1" comes into play.

The transform can be broken down into three distinct components: r="1,0,0,0,1,0,0,0,1" The X vector: r="1,0,0, The Y vector: 0,1,0, The Z vector: 0,0,1"
These three vectors are defined in terms of the editor's global x, y and z axes. You can see that the x vector has an x component of 1 with a y and z of 0. Similarly, the y and z vectors only have length in the global y and z axes, meaning the transform r="1,0,0,0,1,0,0,0,1" can be thought of as the "default" orientation with each axis pointing in their respective global directions.

If you placed a part, then rotated it clockwise 90 degrees and opened its XML, you would see
r="0,0,-1,0,1,0,1,0,0" X vector: r="0,0,-1, Y vector: 0,1,0, Z vector: 1,0,0"
Here, the x vector, which has been rotated clockwise by 90 degrees, is now facing in the -z direction as shown by the -1 in the place of the vector's z component. Similarly, the z vector is now facing in the +x direction as shown by the 1 in the vector's x component. Because the rotation was done around the y axis, the y vector remains the same.

This transform was designed to only ever take either 1 or 0, with there only being one 1 per vector. However, thankfully, the developers decided against including a check to make sure this is the case. Various combinations of stretches and skews can be accomplished by changing the directions of the x, y and z axes defined in this transform.

This is a core concept you need to understand to fully utilize Pipe Theory. If you would like a visual representation of this, check the guide linked at the beginning of this section.
Pipe connections (according to the game engine)
To avoid confusion between vectors and components, all further references to the local x, y and z vectors will use the letters i, j and k instead.

The transform is used for more than just visually rotating and transforming the mesh of a part. Some parts, pipes being the main example, need to have their ends facing the correct way to connect with neighboring pipes.

Because each connecting face of a pipe exists in the center of a face, we can associate them with the part's local axes rather than its faces. For example, the basic angle pipe has a connecting face in the local -x direction and local +y direction, meaning left and up respectively. You can see the locally defined directions of these connections if you place a pipe in the editor without rotating in any direction, leaving the transform as r="1,0,0,0,1,0,0,0,1".

A connection between two pipes will be counted as valid if the rotations and positions of the pipes meet these few base level observations:
  1. The vectors for both connecting faces align, with one pointing in the opposite direction
  2. The pipes are placed next to each other
  3. The pipes are on the same subgrid (this isn't important for this guide, but the list wouldn't be complete without it)

The "vector for a connecting face" will always be one of the local axis vectors, though sometimes pointing in the opposite direction.

For example, on the basic angle pipe component, one of the connecting faces points in the +y direction, making its vector equal to the local y vector, j. However, the other connecting face points in the -x direction, as shown in the image to the left. This would make its vector equal to the local x vector, i, but pointing in the opposite direction (which can be represented by the multiplication -1*i, or more simply just -i).
There are cases where two connecting faces on a pipe share the same local axis vector, like the straight pipe piece. One of the faces will be the vector i and the other will be the vector -i.
To finish it off, this image shows the connection between the two pipe pieces. You'll notice that the angle piece has been rotated so the "top" (j) connection face is the one connecting to the straight piece. This is to reinforce the idea that these axes, the red, green and blue lines, are the local axes of the part defined in terms of the global axes of the editor, not the global axes of the editor themselves.

There is a huge takeaway from all of this:
Pipe connections only care about the connection vector for the faces and by proxy the corresponding local axis vectors. The other two local axes have no bearing on the connection whatsoever.
Getting goofy with it
Let's see how far this can go.

Here you can see an angle pipe and a straight pipe, just like before, only now the angle pipe is skewed to ♥♥♥♥ and the straight pipe is rotated by 45 degrees.

At first glance, you wouldn't think two lengths of pipe with this kind of size difference would be able to connect and transfer fluid or power, especially considering the blatant gap and culled texture between them. However, because the vectors for the two connecting faces are lined up, facing equal and opposite directions, meeting tip-to-tip this connection is perfectly valid.

While experimenting around with XML'd pipes, before I really knew what the rules were, I tried pipes stretched to different lengths. As you might expect, they would no longer connect if their voxels were placed as they were when they were the default 1 long. However, when they were spaced out so their faces meet end to end, and both pipes had the same length in the connection's axis, they connect. At first I thought that meant the rules for connection were based solely on the textures of the faces being aligned, but as shown above, that's not the case; our set of rules needs to be amended to include cases with lengths greater than 1.
  1. The vectors for both connecting faces align, with one pointing in the opposite direction
  2. The vectors have the same length
  3. The vectors met tip-to-tip, assuming a magnitude of 1 reaches half a block out to the face
  4. The pipes are on the same subgrid (still not important right now)


However, the list is getting a little long now. It would be nice if there was a way to sum up each of these points with a single overarching rule, the consequences of which are what we see.
Pipe Theory, proper
At last, we get to how I believe the game actually functions when it checks for valid pipe connections.

Theorem 1.1:
A state of connection exists between pipes IF AND ONLY IF the components of the connection vector for a connecting face of one pipe, when added to that pipe's voxel position, result in the voxel position of another pipe with a connection vector that satisfies the same condition.

Take for example, two angle pipes, both starting with transforms r="1,0,0,0,1,0,0,0,1". One pipe is placed 5 blocks in front, 2 blocks to the right and 1 block below the other. We know that the j axis vector for the local y axis represents the connection vector for the top connection face of each angle pipe, so we can easily edit the transform without much extra thought.

The voxel displacement from the first pipe to the second can be written as the vector s=〈2,-1,5〉 for 2 blocks to the right, 1 block below and 5 blocks in front. The pipes will connect if a connection vector is equal to this displacement, so setting the first pipe's transform to r="1,0,0,2,-1,5,0,0,1" and the second pipe's transform to r="1,0,0,-2,1,-5,0,0,1" will satisfy that condition for both, creating a connection between them.

Because we just directly edited the default orientation, the mesh of one of the pipes will look inverted thanks to the change in direction of the axis vector. To make prettier pipes, it will require more thought to determine which axis vector corresponds to the connection vector after the pipe has been pre-rotated to a better orientation, and whether the connection vector is facing in the opposite direction by default.

Notice that we only changed the axis vector for one of the connection vectors. With the angle pipe, this leaves one more untouched connection vector that we can use to redirect the pipe, chaining connections between different voxels.

The image to the right shows an angle pipe being used in this way. The large distances involved lead to the mesh of the pipe being stretched to be pretty large as well. Because the connection vector for the side face of the angle pipe is -i, it is replaced by -s, negative the displacement between the high pipe and itself. The high pipe uses the j facing connection, so there's no need to negate the displacement for its transform, leading to both transforms having the same signs for that connection's components.

In-line length changes
You may find yourself in a situation where you need to change from a regular 1-long pipe to a semi-decorative 3-long pipe while preserving the connection between them. This can be accomplished easily with an angle pipe with transform r="1,0,0,3,0,0,0,0,1".

The j facing component is now facing in the i direction (+x) with length 3, while the -i facing component is still facing -i with length 1, providing a connection for both 3-long and 1-long pipes. This kind of transition can be very helpful when you need to maintain a connection without using many voxels, but can't afford the ugly stretched mesh of an angle pipe spanning the entire build. Keep in mind the freedom here; neither of these connections need to be facing straight, they can be skewed to face in whatever direction with whatever length you need.

The mythical Pipe Angle Corner
Because each pipe exists in three dimensions and has three distinct local axes for you to edit, you can create a transition between three distinct types of pipes. This image shows this being used to great effect. An engine's output is being split in to distinct directions by a corner, then those directions are transitioned back to normal pipes with an angle and another corner to split the power off to a very long pipe that brings the power to the other side of the engine.
who up theorizing bout they pipe rn
If you have any questions, or anything needs further clarification (or if you notice any mistakes), don't hesitate to ask in the comments below

like commend subscribe
10 Comments
Sir_James 19 Aug @ 11:20am 
Goofy... but well explained :steamhappy:
Freddo 18 Aug @ 6:13am 
Nevermind, I figured it out...
Freddo 18 Aug @ 5:50am 
Ive tried just doing a simple 1x5 straight pipe connected to normal pipes, yet I get no flow through it...
Kernle Frizzle  [author] 16 Aug @ 1:23am 
what exactly are your pipe transforms?
Bookieman 15 Aug @ 4:43pm 
I just cant get the example fo the 5 block long angled and straight pipe to work, what is goin on
programmer137 15 Aug @ 4:22pm 
Pipe theory heck yeah
MrMereScratch 15 Aug @ 8:47am 
pipenheimer
Pengu 15 Aug @ 6:22am 
LMAO these pictures just look so strange 😂 😭 great explanations though.
Tanko-131- 15 Aug @ 5:56am 
the only thing that makes sense here is that nothing makes sense
SpongeX 15 Aug @ 4:27am 
Bro wtf