This workbench allows you to add textures to architectural objects directly in FreeCAD. No need for third party software, as long as you don't want to produce photorealistic renderings.
I was working on a Arch project in FreeCAD. When the geometry was pretty much finished I decided that the building needs some texturing. This will help dramatically in understanding the overall idea (This is made from wood, there are bricks,...). I know Blender a bit and can produce texture materials that look quite good (at least for me :D) but I am lazy. I don't want to switch back and forth between applications when something changes. And as my goal was not to create photorealistic renderings, I decided to do it in FreeCAD.
This workbench works with:
- FreeCAD v0.18 (Python 3 and Python 2 builds). It might also work with older versions but is not tested so far
- FreeCAD v0.19.x (Python 3)
Apply textures only after you've finished modelling your geometry. Otherwise it might be easily possible that you break your texture mapping later on and have to do it again.
Expand this section to learn about how to add textures to a FreeCAD project
This section gives you a step by step instruction on how to add textures to a FreeCAD project. It will guide you through the process of texturing a small building.
We start here:
and should finally end up here:
-
Download and open the "House.FCStd" file located under Resources/Documentation in the repository. Or start with whatever arch project you want. Now you should see this building in the 3D View
-
Next switch to the Arch Texture Workbench and click the "Create TextureConfig" icon
-
Now the object should be visible in the TreeView. TextureConfigs are hidden by default when we create them and when the document loads. This is done to prevent excessive loading times on startup.
-
Now lets start texturing. Double click the TextureConfig object to display the task panel to set up some textures. After clicking the "Add Material" button, you should see something like this
-
Select
MatBricks
in the Material Combo Box and click the "..." button next to the "Texture" input field. Select a brick texture from your file system (I used textures from https://www.textures.com/). After you click "OK" nothing will happen because the TextureConfig is still hidden. -
Select the TextureConfig in the Tree View and hit the "Space" key. This will add the texture in our config to all objects with the "MatBricks" materials. When hiding the TextureConfig again, the textures will be removed from the 3D View. When the textures are visible you should see something like this
But wait! This does not really look like a brick wall at all. The texture is stretched pretty badly. But this is easy to fix.
-
Double-click the TextureConfig again and add the real size of the texture. The bricks texture I used is about 1200x1200 mm in size. If the size of the texture is not given, simply google the size of a single brick and multiply it with the number of bricks in your texture.
-
Click "OK" and check the 3D View again. Now it looks much more like a real brick wall.
-
Repeat the above steps for all other materials and you should end up with something like this
-
Most of the textures look good. But it might be, that the roof does not look like expected. The texture should be mapped so that the lines run horizontally but they run oblique across the faces.
But this is pretty easy to fix. Select the TextureConfig in the TreeView and click the "Configure Faces" button.
-
Now enter the angle in degrees you want to rotate a certain face. Positive values rotate the texture clockwise and negative values counter clockwise. For our roof a rotation of 55 degrees for the front and back faces and -55 degrees for the side faces should work pretty well. Now select the faces you want to set the rotation for and click "Apply". The rotation is applied immediately. You have to unselect the faces to see the rotated texture.
When something happens and you end up with broken textures or a broken 3D View (e.g. you deleted the TextureConfig before hiding it) you can always close the document and reopen it. The TextureConfig is hidden by default and no textures will be shown after a reload.
Currently Texture Mapping only works with objects that have a Material assigned. All architectural objects have such a property. To create a material, go to the Arch workbench and create a material there. See https://www.freecadweb.org/wiki/Arch_SetMaterial for further details.
It is also not possible to texture faces of a single object individually. All faces will get the same texture based on the material assigned.
Note: From FreeCAD 0.19 onwards, it is possible to also texture objects created with other workbenches than the Arch workbench.
- Select the object in the TreeView
- Right click in the Data Tab and click "Show all"
- Right click again and click "Add property"
- In the Group field add
Base
.
In the Name field addMaterial
- Now you can assign a material to the object like you would for an Arch object
When mapping the texture to a face the algorithm works as follows:
- When the real size is set and the texture is not quadratic, the algorithm maps the longest side of the texture to the longest side of the face
- When the real size is not set or the texture is quadratic, the algorithm maps the "s" side of the texture to the longest side of the face
- When a override is set for a face, and the override has a rotation other the 0, this rotation will be used to rotate the texture on this face
- xwd
- tiff
- tga
- png
- pic
- jpeg
- JPEG-2000
- gif
- rgb
- eps
Bump mapping is a technique to add a lot more details to an object without actually modelling it. It is best explained with an example.
Lets say we have a simple wall with a brick texture applied:
The wall looks a bit flat. Normally there are bumps in the bricks and shadows in the gaps. Modelling this small details will need a long time to finish and will increase rendering time a lot. But with bump mapping we get a lot of details nearly for free. This is the same object with the same texture but an additional normal map applied:
This looks a lot more realistic.
There are two types of bump maps available:
- Height Map: This is a simple greyscale texture that contains height information per pixel. The darker the pixel, the deeper inside the surface it is.
- Normal Map: A normal map is a bluish type of texture. This map looks more realistic than a height map as it contains more information than a height map. The color of each pixel defines the normal vector for this part of the surface. So light calculation could be a lot more accurate.
To enable bump mapping, simply select a bump map texture file for a given material. Bump mapping only works in combination with a texture. When you select a bump map only, nothing will be displayed at all.
Expand this section to learn more about Environment Textures
When you have done some texturing and want to show off your work, the simplest way would be to create a screenshot inside FreeCAD. You position your camera perfectly and take a shot.
Not that exciting, right? The default background might work when building a cloud-castle ;)
But for buildings on earth, some natural background would be way better. The environment config exactly does this.
With the right config you can enhance your screenshots a lot and get something like this:
To create a new EnvironmentConfig
you click the "Create EnvironmentConfig" command.
Result: This will create a new EnvironmentConfig
object in the TreeView with some default settings applied. You can show and hide a EnvironmentConfig
object like other objects in FreeCAD.
You might notice, that event though the object is visible, nothing is shown in the 3D view. This is because no textures are configured yet. Only when a texture is configured for a given part of the environment will this part will be displayed.
To understand the geometry properties a bit better take a look at this picture
The green line is the geometry that displays the panorama image, as seen from top view.
When PanoramaType
is set to Thirds
the full image will be distributed evenly across the three planes.
When PanoramaType
is set to 360
you can imagine the full image to be mapped to the blue circle. And only the part that maps to the green planes is displayed. So when you rotate the environment config the visible panorama also changes. The beginning of the image is considered "North" and is always fixed to the positive Y axis.
This is the image that should be displayed on the ground plane. This should be a quadratic image as the ground plane is also quadratic. The size of the ground plane is calculated according to the radius and length properties.
This image is displayed on the green line in the above picture. You can define the length and height of this plane with the properties of the same name.
This is the image that is displayed above the panorama image as the sky. The length is the same as for the panorama Image. The height is calculated according to the sky overlap, radius and length properties.
The type of panorama image used.
Thirds
: The full image will be distributed evenly across the three planes360
: The image will be treated as 360 degrees panorama
See the introduction for more details.
Defines the Length of the plane that shows the panorama image. The plane consists of three evenly sized faces.
Defines the height of the plane that shows the panorama image.
This is the radius of the blue circle in the picture above. Basically this defines the distance from the origin to the panorama image plane.
The Sky plane is positioned similar to the panorama plane. It is offset from the panorama plane by 1 meter (The blue circle will have Radius + 1m) and continues above the panorama plane in a 45 degree angle to the origin.
This property defines the distance, the sky plane should run down behind the panorama plane. This is especially useful when you have a panorama with transparency where the sky should be. Then your sky is visible where the panorama is transparent.
The image above displays the default placement of the environment textures. The rotation can be used to rotate the whole environment around the Z axis. You can use this to orient the environment to face your camera.
This property can be used to move the environment up and down the Z-Axis. This is useful to have the ground plane displayed below your geometry.
By default this property is set to -1mm so that it is below all geometry on the origin plane.
Expand this section to learn some insight on the technical part of the workbench. This is mainly some documentation for me so that I still know in a year or two what's going on in this workbench. But maybe some aspects could be interesting for others too.
First, it is relative easy to add textures to objects in FreeCAD. An example found on this FreeCAD forum thread shows adding a texture is only 3 lines of code. But... mapping textures right on to an object involves a bit more code.
- Create a SoTexture2 object and set the
filename
to a image file - Create a SoTextureCoordinate2 object and set the points array to map the vertex coordinates of the geometry
- Add both to the rootNode of your object and the texture should show up
The texture config holds all the information about materials and the textures to apply to them. When displayed the textures will be added to the objects, when hidden the textures are removed.
The texture manager does the heavy lifting. It keeps track of all textures and the textured objects and can add/remove textures to/from objects.
When texturing objects the texture manager looks for arch objects with a material assigned. When the material is found in the texture config it will use the settings to texture the object.
The texturing process is as follows:
- We get the
RootNode
of the object - We search for the
Coordinate3
node in theRootNode
. This node contains a list of all vertices our object consists. - We search for the
SoBrepFaceSet
in theRootNode
. This is the object that contains the face information- This object has a list of vertex indices that map to the vertices in the
Coordinate3
object - It also has a list of faces. This describe the number of triangles that form a face of the object.
- It also contains a textureCoordinate field that works like the coordinate indices but for textures. This should normally be the same as the coord index field or it should be empty But FreeCAD sets it to -1. So we have to override it with the
coordIndex
field to get correct textures.
- This object has a list of vertex indices that map to the vertices in the
- Based on the
FaceSet
and theCoordinate3
object we calculate the vertices that make up each face.- We group the vertex indices by triangles. Each triangle is separated by a
-1
. - Then we use the
partIndex
field to get the number of triangles per face and build the face list from this information
- We group the vertex indices by triangles. Each triangle is separated by a
- When we have the faces of our object we need to calculate the texture coordinates for this face. See Calculating texture coordinates for further details.
- When we have all the information we need, we simply add the required nodes to the scenegraph and the textures show up.
This is the trickiest part in the process. The basic idea is pretty simple:
- Move each face to the origin
- Rotate each face that is maps the XZ plane
- Move each face so that it is in the positive X and Z quadrant
- Calculate the bounding box for the face
- Map the image to match the bounding box
This is pretty straight forward. As we know the first three vertices of our face always form a triangle, we use the first one as our offset and subtract it from each vertex in the face. So the first vertex matches the origin and the others, moved by the same amount, still form our original face.
This was pretty tricky to figure out (at least for me as I'm not a specialist in Matrix transformations and so on).
The general idea behind it is:
- Calculate the local coordinate system for our face
- Create a matrix that transforms our local coordinate system to the global one
- Multiply each vertex with the matrix
Calculate the local coordinate system:
- We make use of the fact, that the first three vertices for a triangle. This triangle is our local coordinate system.
- The Y-Axis maps to the triangles normal vector. This ensures, that the normal will face the Front plane later on as this is also the Y axis in the global coordinate system.
- The X-Axis is the shortest line starting from the first vector. This ensures that we don't use the diagonal of the triangle as our axis. Else the face would be twisted in the front plane.
- The Z-Axis is simply the cross product of the other two axis
Calculate the matrix:
- First we normalize our local coordinate system. Otherwise we would scale our face when mapping it to the front plane
- Then the rotation matrix is simply a dot product of the normalized local axis and the global axis
FreeCAD.Matrix(normalizedX.dot(globalX), normalizedX.dot(globalY), normalizedX.dot(globalZ), 0,
normalizedY.dot(globalX), normalizedY.dot(globalY), normalizedY.dot(globalZ), 0,
normalizedZ.dot(globalX), normalizedZ.dot(globalY), normalizedZ.dot(globalZ), 0,
0, 0, 0, 1)
Now we can end up with faces that have vertices with a negative Z or X value. We want them all to be positive so that we can use this information later on and simply use our bounding box to calculate the texture coordinates.
To do so we check the Minimum X and minimum Z values of our face. If one is less than 0 we transform all vertices in the face by this amount in the positive direction. Now the smallest values will be 0 and everything else should be in the positive axis.
Now that everything is in the positive XZ plane we can simply use the smallest XYZ and biggest XYZ values to form our bounding box.
Basically the image should map our bounding box. That means the lower left corner of the image maps to the lower left corner of our bounding box (XMin, YMin, ZMin) and the upper right corner of the image maps to the upper right corner of our bounding box (XMax, YMax, ZMax).
When the user sets the realSize
property of the texture config, we use this information to calculate a scale for the image first. Lets say the face is 2x2 meters in size. And the image has a real size of 1x1 meters. Than we have to repeat the texture 2 times in each direction to get it scaled right.
After we know how big the image should be we simply calculate each vertex coordinate relative to the bounding box. Lets say we have a vertex in the middle of our image. It should map to the 0.5/0.5 coordinates of the image.
Found a bug? Have a nice feature request? Simply create an issue in this repository or post to these FreeCAD Forum threads:
- Bugs/Help Requests: https://forum.freecadweb.org/viewtopic.php?f=3&t=38222
- New Features: https://forum.freecadweb.org/viewtopic.php?f=10&t=38223