ArmorPaint Is a Dataflow, Not Just a Brush
ArmorPaint is a 3D physically based rendering (PBR) texture-painting application. The question here is not simply what tools it offers, but how one edit becomes a coherent surface in the viewport, a reusable project, and an exportable texture set. Understanding that path gives you a practical way to read the rest of this series: you can ask what representation carries an idea, which subsystem transforms it, and where the transformation stops being lossless.
That question matters because a PBR surface is not one picture. It is a coordinated description of how a mesh reflects light, how opaque it is, how rough or metallic it appears, how its small-scale direction changes, and how height or other effects contribute. ArmorPaint’s distinctive architectural problem is therefore to keep several kinds of meaning aligned while someone is editing the surface interactively. The repository shows that problem being solved as a connected dataflow rather than as a painting canvas with a renderer attached afterward.
The subject is a working texturing application
The repository presents ArmorPaint directly: software for 3D PBR texture painting. It is also explicit about its audience. This is the developer-oriented source repository, and the project notes that distributed binaries help fund development. That distinction is useful. The repository is not a product brochure or a neutral description of texture painting; it is the implementation of a complete application, including its engine layer, platform backends, assets, build system, file handling, and editor-specific behavior.
Its broad organization makes the first relationship visible. The base side contains a general-purpose 3D engine, graphics and operating-system backends, helper libraries, build tools, and shared assets. The paint side adds ArmorPaint-specific code and content. The separation is not the same as a separation of products. ArmorPaint relies on the base layer to create windows, manage GPU resources, run shaders, load data, and present frames. The base layer, in turn, becomes meaningful here because the painting application gives it a demanding workload: editable surfaces, many texture targets, interactive input, previews, and export.
That makes ArmorPaint the subject, while the engine and build layers are key relationships. A useful explanation must keep returning to what those relationships reveal about the application. The graphics abstraction matters because ArmorPaint wants the same painting model to reach several targets. The project file matters because the application needs to reopen an editable scene rather than only display a finished image. The material graph matters because a surface description must become executable GPU behavior. None of those subsystems replaces ArmorPaint as the thing being studied.
The repository also supplies a boundary for what can be concluded. It is strong evidence for structures, control flow, render passes, resource ownership, file lifecycle, and configured feature gates. It cannot, by itself, establish how fast a particular GPU renders, how every external file is interpreted, or whether the application is better than an alternative. Keeping that boundary visible is part of understanding the system accurately.
The central problem is not drawing a stroke
A simple painting program can treat a stroke as colored pixels placed on a bitmap. ArmorPaint cannot do that without throwing away much of what makes 3D PBR painting useful. A stroke may need to land on a curved mesh, respect the surface depth seen by the camera, affect several PBR channels together, pass through masks or layer opacity, and remain visible through different viewport modes. The same result may later need to be saved with its layers and materials intact or translated into the channel layout expected by another tool.
The important architectural idea is that one user intention can have several representations. The editable project holds objects, material and brush graphs, layers, groups, masks, channel textures, and assets. GPU resources hold the data in forms that shaders and render passes can sample or write. A saved project preserves relationships so they can be reconstructed. An export path may deliberately flatten those relationships into a smaller set of images. These are not interchangeable copies. Each representation is shaped by a different job.
ArmorPaint’s dataflow can be sketched at a high level:
The arrows do not mean that every feature follows one identical path. They show the dependency that matters: editor intent must enter a project model before it can be reused across rendering, persistence, and output. A material graph can influence generated shader behavior; a layer can provide channel data to that shader; a render pass can combine the result with lighting and geometry; an export operation can flatten selected layers into a target-specific arrangement. The connections are more informative than any one class or file name.
For example, “make this object look worn” may begin as a material choice, use a layer and mask to limit its scope, become GPU work in the viewport, remain editable in the project, and eventually become selected texture channels for another tool. The visible surface is only one result of the decision. The other results preserve, execute, or translate the same intent.
This is also why the application’s “brush” is not the whole story. Brush logic describes how an interactive tool shapes a gesture, while material logic describes what the surface contributes to base color, normal, roughness, metallic, opacity, height, and related outputs. Layers determine where those results belong and how they combine. The visible stroke is the point where these meanings meet, not the origin of the whole system.
One representation is not enough
The project model is the first place where this design becomes concrete. ArmorPaint keeps a paintable object set, material slots backed by node canvases, brush slots backed by logic canvases, and an ordered layer structure. A layer may be an ordinary paint layer, a group, a mask, a filter-like layer, or a layer driven by a material. It can carry visibility, blending, opacity, object selection, UV choices, and channel participation. The model is richer than a stack of color images because it must preserve the reasons a surface looks the way it does.
The channel split is especially revealing. A paint layer has coordinated targets for color and opacity, normal information, and packed data used for occlusion, roughness, metallic, and height values. This arrangement is not merely a storage preference. Later GPU code can sample the relevant target for the current operation; history can preserve the targets needed to undo; flattening can merge them into export images; and a material node can expose them as meaningful outputs. A change to one part of the model therefore has consequences across rendering and interchange.
The same principle explains why assets and meshes belong in the picture. A texture is not useful only because it can be displayed; it may be referenced by a material graph, used as a brush mask, packed into a project, or assigned as an environment map. A mesh is not only geometry; its UVs, object identity, transform, and relationships affect where painting lands and how layers are selected. ArmorPaint’s architecture keeps these relationships in a project runtime so that the editor, renderer, and file lifecycle can refer to the same underlying objects.
That integration creates leverage. A material can be edited once and used for previews, fill layers, viewport rendering, and later export preparation. A layer mask can affect the surface without being baked immediately into the base texture. A project can retain enough structure to reopen the decision rather than merely reproduce its last visible frame. But the leverage is conditional. Every additional relationship must be synchronized, serialized, rebuilt, and translated when it crosses a boundary.
The practical consequence is a question about ownership
The most useful way to examine ArmorPaint is to follow ownership rather than to memorize components. Ask who owns the meaning of a surface, who owns the timing of an edit, who owns the GPU representation, and who owns the final translation into a file. The project model owns editable meaning. The runtime and context coordinate what is active. The render system turns that state into GPU work. Persistence reconstructs the relationships later. Export chooses which meaning can be preserved in an external texture format.
This view also makes the central trade-off visible. Keeping authoring, rendering, and output close together can make features reinforce one another: a layer can feed a shader, a shader can be used by a preview, and the same state can be saved or flattened according to the user’s goal. The cost is coupling. GPU resource limits can shape how layers are rendered. Platform differences can gate features. External assets can break a project’s portability. A rich project file can preserve more intent but demands more reconstruction logic than a bitmap does.
The repository can show these mechanisms and boundaries; it cannot turn them into universal performance claims. That restraint is not a weakness in the model. It tells us how to use the evidence: understand what ArmorPaint has chosen to connect, then examine the conditions under which each connection works.
The next unresolved dependency is time. If ArmorPaint’s surface meaning is distributed across project state, logic, GPU targets, previews, and files, how does the running editor keep those representations synchronized while a frame is being produced? The answer begins with the application loop—the path from startup and input through update ownership to the frame that finally reaches the screen.