Repository navigation
Vertex painting - #63
Merged
Merged
Conversation
Paint profiles (ChiselPaintProfile) say what paint means to a shader: modes, each a Color or a named Value, written to a vertex attribute (Color or TexCoord2-7) in some of its components (xyzw, xyz, or one). A generator keeps its surfaces' paint (ChiselSurfacePaint): per surface and attribute a grid of values in the surface's plane, in its own space; the cells are also the surface's subdivision. ChiselPaintManager gives each model's tree its paint in the model's space (CSGTree.SetPaint) and rebuilds the brushes whose paint changed. A render mesh with painted surfaces takes the attributes painted on it in a second vertex stream; its unpainted surfaces take the default. A mesh without paint keeps its layout. The paint of a surface is part of its hash, so a mesh that changes only in paint is not kept. Test shaders and materials with their profiles: Chisel/Test/Paint Color (vertex color) and Chisel/Test/Paint Value (TexCoord2.x).
Each grid point of a surface's paint inside the surface becomes a vertex (PaintSubdivision), so the paint has vertices to land on: it is inserted as in an incremental Delaunay triangulation, splitting the triangle it is in or the two on the edge it is on, and the edges round it are flipped until the triangles round it are Delaunay. Points on or near the outline, or near a vertex, are left out, so the outline isn't cut and the surfaces it touches get no T-junctions. Only surfaces in the grid's plane or parallel to it are subdivided, at most 4096 points each.
The Chisel Paint tool (Tools > Chisel > Paint) paints the surface under the mouse in a mode of the paint profile of its material's shader: a color, or a named value, with a round brush (radius, strength, hardness) that paints, adds, subtracts, smooths or erases. A surface painted for the first time gets a grid over it of the tool's cell size (ChiselPaintBrush). Only the hit surface is painted; a stroke is one undo. Fill Surface sets the whole surface, control-click samples the paint under the mouse, and the channel buttons copy, swap or invert components. Modes in one group (ChiselPaintMode.group) are weights that sum to one: painting one takes from the others in their proportion.
Picking renders, and the tool picked on every event, also inside the scene
view's layout and repaint ("GUI Window tried to begin rendering while
something else had not finished rendering"). It now picks on mouse move,
down and drag only, and draws what it last picked.
The panel was a GUI window, which needs the scene view's BeginWindows; it is
a plain area now, and shows the modes of the surface last painted on, so
they stay while the mouse is over the panel.
The settings were drawn straight into the scene view, see-through and not like the other tool windows. They are a Chisel Paint overlay now, shown while the tool is active.
A mode shows what its kind uses: a color mode its default color, a value mode one default for its components, its range and its group; the components are toggles, named RGBA for the Color attribute. A profile says when it has no shader, and warns when two modes write one component of one attribute. Also: FindObjectsByType without the obsolete sort mode in the decal editor.
A painted surface's outline is cut where the grid meets it (its points on it, and where its lines cross it), so the surface is the grid's cells right up to its edges instead of fans from its corners. A point is put on an outline edge within a twentieth of a cell, on an edge inside only when it is on it. Over a surface whose shader has no paint profile the tool shows no brush but a not-allowed cursor, with a solid tooltip saying why; the overlay keeps one width.
The profile registry (ChiselPaintProfiles) moved to the components, with profiles registered in code next to the project's profile assets. Paint of a surface whose material's shader has no profile with a mode in its attribute is kept on the generator, but doesn't subdivide the surface or reach its mesh; it comes back with a material that takes it.
The spacing of the points a surface is painted at is the surface's Paint Spacing (ChiselSurface, beside its material: only painting reads it, so it stays out of the details every brush polygon carries), shown for materials whose shader has a paint profile. Changing it resamples the surface's paint onto a grid of the new spacing. The paint tool's overlay shows every mode of the project's profiles, by name, as wrapping toggle buttons; a surface takes the chosen mode when its shader's profile has one of that name. Over a surface that doesn't, the tooltip says why, on three lines.
The paint profiles are plain data in one project asset (ChiselPaintSettings, Assets/Chisel/ChiselPaintSettings.asset), edited in Project Settings > Chisel > Paint: the shaders, each with its remove button, and the chosen one's modes. A material's Chisel section says whether its shader is paintable, and its Configure Paintability Of Shader button opens that page on the shader's profile, adding one when it has none. The package's per-shader profile assets are gone; a new settings asset starts with the test shaders. The paint tool is one of the Chisel tools, with a brush icon. Its overlay shows the operations as wrapping toggles with icons (Add and Subtract only for a value), swatches and the recently painted colors under the color, and a thumbnail of the blob a dab paints next to strength and hardness.
The color's swatches are kept per user and wrap over as many rows as they need; the chosen one is outlined, each has a border, a drag reorders them and a middle-click removes one. The last cell, the current color with a plus over it, adds a swatch and opens Unity's color picker on it. The operations are icon toggles (named in their tooltips) under Operations; the Mode label is gone. The not-paintable cursor stays where the scene had the mouse, so it isn't shown over the overlays. The settings page lists a profile's modes as a list with a header instead of a foldout.
As in Unity's color picker: no plus, the current color drawn smaller than a swatch inside an empty cell.
Swatches are 14 pixels and share their border with their neighbours; the chosen one is outlined inside its cell.
Once per domain, before the first brush registration, the sizes and offsets of BrushMeshBlob.Polygon, InternalChiselSurface and SurfaceDetails are measured managed and in a Burst-compiled job; a difference is logged as one error naming the fields. Growing SurfaceDetails once left code compiled for the old layout writing blobs managed code misread (polygons 8 bytes short), which broke every brush.
Fill is one of the operations (a bucket icon): clicking a surface sets all of it to the color or value, and a stroke fills each surface it goes over once. The brush settings are disabled while it is chosen; the Fill Surface button is gone. Swatches hold a color once, compared as they are kept (8 bits a component): adding the current color when a swatch has it already chooses that swatch, and a color picked into a new swatch that another swatch has takes that one. The add cell takes only a left click. A right click on a swatch opens a menu to delete it, for those without a middle mouse button. The swatch borders are more subtle.
The icon script wrote them again under the old name with spaces; the tool loads paint_tool.
The surface's own paintSpacing field showed again under the details drawer (and for surfaces that can't be painted).
A surface render buffer kept the brush's entity with its painted surface index; the entity is now the brush buffer's, once, and a surface keeps only its index (-1 without paint). The paint lookup says whether there is any paint with a flag set when it is made, rather than asking its hash map for every surface. Comments that described removed parts (the Fill Surface button, the cell size, the profile assets) are brought up to date.
A render mesh drawn with a material whose shader has a paint profile gets the profile's attributes, at their modes' defaults where nothing is painted, so the shader never reads an attribute the mesh lacks. Materials are registered as surfaces take them (ChiselPaintMaterials, as the hotspot store does); what their shaders take comes from the paint profiles, and a change to it rebuilds every brush once, before the next build. A surface's hash takes its material's paint attributes, so their meshes are written again.
Copying, swapping and inverting components of a surface's paint is gone from the overlay, and with it from the paint brush.
The tree keeps its paint with the ranges and hashes an update looks up, made when the paint changes rather than copied and hashed at every update; an update reads views of it. Paint is replaced one entity at a time: only that entity's layers are hashed, its old values are left behind until they are half of all values, then compacted. The materials' paint attributes are copied to the tree only when they change.
The triangle across an edge comes from a map of edges kept with the triangles, and a point is found by walking from the triangle the last point went into (the grid's points come row by row), rather than by scanning every triangle for each; a point the walk doesn't find is still looked for in every triangle. A grid at the most points a surface gets is tested too.
Paint doesn't change a brush's shape, so its brushes are marked with a new surfaces-only flag (SetSurfacesDirty) instead of as reshaped: the brush is built again, the brushes touching it aren't.
A generator says its paint changed (PaintChanged, its paint set or removed, enabled, disabled, validated) and only those, and the painted generators that moved, are given to their trees again, one entity at a time (CSGTree.SetPaint with an entity). Every generator is looked at only when what shaders take may have changed. A generator that loses all of its paint, or is disabled, has its paint taken from the tree and its brushes built again.
A grid point keeps only the components its paint has, in the generator's grid, in the tree's values and in what a vertex samples (x, y, z, w in that order), so the generator's values go to the tree as they are. In the render mesh the color is 4 bytes (UNorm8) and a texture coordinate has floats up to its last painted component; the materials' paint attributes carry their components for that.
A surface whose grid isn't whole (its values didn't load) gets a new grid when it is painted, rather than the brush stopping at it. The paint manager only keeps the generators with paint, or whose paint a tree has, so a destroyed model's meshes aren't kept loaded through the others.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #36.
Vertex painting for Chisel surfaces: paint is written into vertex attributes (the color, or a texture coordinate) that the surface's shader reads, so what paint means is up to the shader.
Paint profiles
Chisel/Test/Paint Color,Chisel/Test/Paint Value) show color and value paint.Paint on surfaces
Chisel Paint tool
Cost