HammeriteBrush

Core: the map. Inherits Resource

A convex volume bounded by HammeriteFace planes: the unit a HammeriteMap is authored in.

A brush is its faces' planes, normals pointing out; its corners are worked out from them and cached. In the subtractive world an AIR brush carves a room out of solid and a SOLID one fills it back in, in order; a detail brush is dressing that carves nothing. A brush made here is free-standing until HammeriteMap.add_brush(), which also gives it its map; coordinates are map space. A box of a size is from_aabb(), or HammeriteMap.create_box_brush() to put one in a map. What each edit says: - geometry_changed: a face's plane moved (HammeriteFace.plane, move_face_to(), set_face_distance()). The map recompiles where the brush is. - face_attribute_changed: a face's look - texture, UVs, lightmap. The brush is remeshed; nothing recompiles. - tier_changed: what the compiled world is made from - volume_type, is_detail, fill, priority, interior_exterior. The map recompiles. - temporary_changed: a tool preview committed or made (is_temporary). - property_changed: one of properties, by its registered key. The map recompiles only if the property's schema says the compiled world reads it. Changing a face's plane by hand with HammeriteFace.set_plane_no_emit() says none of this; call request_recalculate_all() afterwards. selected, hovered, temporary_transform, is_temporary and ghosted are the editor's state; hidden is too, but it is saved and takes the brush out of the compiled world.

Properties

TypeNameDefault
intbrush_entity_id0
ShaderMaterialbrush_material
booldirty
HammeriteFace[]faces[]
StringNamefill&""
boolghostedfalse
ShaderMaterialghosted_brush_material
intgroup_id0
boolhiddenfalse
boolhovered
intid0
intinstance_id0
intinterior_exterior0
boolis_detailfalse
boolis_temporary
intlayer_id0
HammeriteMapmap
intorder0
intorder_sequence0
intprefab_id0
intpriority0
Dictionaryproperties{}
boolselected
Transform3Dtemporary_transform
ShaderMaterialtransparent_brush_material
intvolume_type0

Methods

ReturnsMethod
voidadd_face(face: HammeriteFace)
voidapply_lightmap_uniforms(mat: ShaderMaterial, face: HammeriteFace) static
ShaderMaterialbuild_face_material(face: HammeriteFace, ghosted: bool = false, is_water_entity: bool = false, entity: HammeriteEntity = null, fill: StringName = &"") static
floatcalculate_face_area(face: HammeriteFace)
voidclear_property(prop_name: StringName)
HammeriteBrush[]clip(clipping_plane: Plane, clip_texture: Texture2D)
floatclip_epsilon() static
PackedInt32Arraycompute_indices(num_vertices: int)
Vector3[]compute_vertices(face: HammeriteFace, ignore_cache: bool = false)
boolcontains_point(point: Vector3, epsilon: float = 0.001)
HammeriteBrushcopy()
voidcopy_placement_to(other: HammeriteBrush)
voidcopy_tier_metadata_to(other: HammeriteBrush)
intcsg_order_key()
HammeriteBrushcube(origin: Vector3, texture: Texture2D) static
HammeriteBrushfrom_aabb(box: AABB, texture_name: StringName = &"", volume: int = 0) static
Meshgenerate_mesh(for_collision: bool = false)
Meshgenerate_outline_mesh()
AABBget_aabb()
HammeriteBrushEntityget_brush_entity()
floatget_chroma_bleed() static
Variantget_entity_property(prop_name: StringName, default = null)
Vector3get_face_average()
Vector3get_face_centroid(face: HammeriteFace)
HammeriteFaceget_face_from_normal(normal: Vector3)
Variantget_property(prop_name: StringName, fallback = null)
ShaderMaterialget_sky_material() static
ShaderMaterialget_surface_material() static
Vector3[]get_vertices()
boolhas_property_override(prop_name: StringName)
boolhas_transparent_face()
voidinherit_csg_position_from(source: HammeriteBrush)
PackedVector3Arrayintersect_segment(from: Vector3, to: Vector3)
Vector3[]intersect_vertices(plane: Plane)
boolintersects_brush(brush: HammeriteBrush)
voidinvalidate()
boolis_convex()
boolis_filled()
boolis_filled_with(fill_id: StringName)
boolis_owned_by_entity()
voidmirror(axis: int, coordinate: float)
boolmove_face_to(face: HammeriteFace, distance: float)
voidrecompute_face_vertices()
voidrequest_delete()
voidrequest_recalculate_all()
voidrequest_recalculate_collision(recompute_verts: bool = true)
voidrequest_recalculate_mesh(recompute_verts: bool = true)
voidset_chroma_bleed(value: float, map: HammeriteMap = null) static
voidset_face_distance(face_idx: int, dist: float)
voidset_property(prop_name: StringName, value)
voidset_sky_material(value: ShaderMaterial) static
voidset_surface_material(value: ShaderMaterial) static
ShaderMaterialstock_brush_material() static
ShaderMaterialstock_ghosted_brush_material() static
ShaderMaterialstock_sky_material() static
ShaderMaterialstock_transparent_brush_material() static
HammeriteBrush[]subtract_brush(brush: HammeriteBrush)
boolvalidate_geometry()
PackedByteArrayvalidate_geometry_of(brushes: HammeriteBrush[]) static
floatvertex_weld_epsilon() static

Signals

delete_requested

signal delete_requested()

Emitted when brush has requested its deletion.

entity_id_changed

signal entity_id_changed()

Emitted when brush entity id has changed.

face_attribute_changed

signal face_attribute_changed()

Emitted when face attribute has changed.

geometry_changed

signal geometry_changed()

Emitted when brush geometry has changed.

ghosted_changed

signal ghosted_changed(ghosted: bool)

Emitted when brush ghosted state has changed.

hidden_changed

signal hidden_changed(hidden: bool)

Emitted when hidden changes, with the new value: true when the brush has been hidden.

hover_changed

signal hover_changed(hovered: bool)

Emitted when brush hover state has changed.

layer_changed

signal layer_changed(old_layer_id: int, new_layer_id: int)

Emitted when brush layer has changed.

property_changed

signal property_changed(prop_name: StringName, value)

Emitted when an extendable brush property has changed - see properties. Carries the EFFECTIVE value, so a property reset to its default reports the default rather than nothing.

selection_changed

signal selection_changed(selected: bool)

Emitted when brush selection has changed.

temporary_changed

signal temporary_changed(is_temporary: bool)

Emitted when the brush stops (or starts) being a tool preview - see is_temporary.

temporary_transform_changed

signal temporary_transform_changed(transform: Transform3D)

Emitted when temporary transform has changed (used during transform tool operations).

tier_changed

signal tier_changed()

Emitted when this brush's subtractive tier changes (volume_type or is_detail), or anything else the compiled world is derived from: fill, priority and interior_exterior. The map invalidates its compiled worldrep on this, and views re-route the brush between the worldrep (structural) and per-brush (detail) representation.

Enumerations

enum VolumeType

ValueNameDescription
0SOLIDFills its volume back in with rock.
1AIRCarves its volume out of the rock: a room, or part of one.

enum InteriorExterior

ValueNameDescription
0AUTOWorked out by the compiler: a room reaches the sky if it can see a sky face through its openings.
1INTERIORIndoors whatever the compiler finds.
2EXTERIORReaching the sky whatever the compiler finds.

Property descriptions

brush_entity_id

var brush_entity_id: int = 0

The HammeriteEntity.id of the HammeriteBrushEntity that owns this brush, or 0 for world brushwork. Emits entity_id_changed. Set through HammeriteMap.assign_brush_to_entity() rather than directly, so the entity's own list agrees.

brush_material

var brush_material: ShaderMaterial

Instance aliases of the shared material resources (kept for any external references). The static build_face_material() uses the consts so it can run without a brush instance (the cell mesh builder has only loose boundary faces).

Experimental: The renderer's, not part of the supported API.

dirty

var dirty: bool

True while the cached face corners and bounds may be stale. Cleared by recompute_face_vertices() and validate_geometry(); set by invalidate().

Experimental: The brush's corner cache, not part of the supported API.

faces

var faces: HammeriteFace[] = []

The bounding faces. Assigning the array adopts each face (HammeriteFace._brush) and listens to it; appending to it in place does neither, so assign a whole new array or use add_face().

fill

var fill: StringName = &""

What fills this air brush, by HammeriteBrushFill id - water, or whatever a game registered. Empty is a room full of nothing, which is nearly all of them. Only air can be filled: a fill says what is IN a room, so it means nothing on the rock around it. Deliberately NOT a third VolumeType - a filled brush still carves, still becomes a cell and still grows portals, so everything that asks whether a brush is air must keep its answer.

ghosted

var ghosted: bool = false

Is this brush ghosted (rendered transparently for reference). Takes precedence over normal rendering but not over hidden.

ghosted_brush_material

var ghosted_brush_material: ShaderMaterial

Instance alias of stock_ghosted_brush_material(); nothing in Hammerite reads it.

Experimental: The renderer's, not part of the supported API.

group_id

var group_id: int = 0

The editor group this brush is selected together with, or 0 for none.

hidden

var hidden: bool = false

Hidden by an editor tool. Saved, and it takes the brush out of the compiled world while set. Emits hidden_changed.

hovered

var hovered: bool

Editor hover state, not saved. Emits hover_changed.

id

var id: int = 0

ID of the brush. The brush ID should always be unique. To generate a new ID, use HammeriteMap.gen_brush_id().

instance_id

var instance_id: int = 0

The HammeritePrefabInstance.instance_id this brush belongs to, or 0 if none.

interior_exterior

var interior_exterior: int = 0

Interior/exterior override for this air brush, consulted by HammeriteMap.cell_reaches_sky(). Changing it emits tier_changed, so the map works out again how far the sky reaches. AUTO derives it: a room is exterior if it can reach the sky through a chain of openings, which is what lets daylight into a room with a window. INTERIOR and EXTERIOR overrule that outright, and INTERIOR also stops the derivation spreading past this room - which is how you seal a cellar off from the lit courtyard it opens onto. Note this is the LIGHTING question. Acoustics asks a stricter one, and has its own override, since a room with a window is lit from outside but is not outdoors. SOLID brushes ignore this field.

is_detail

var is_detail: bool = false

Detail brushwork (additive, in-cell dressing) vs structural shell. Detail brushes do not carve cells or portals; they render/collide via the per-brush pipeline. Changing it moves the brush between the worldrep (structural) and per-brush (detail) representation.

is_temporary

var is_temporary: bool

Is this brush temporary (a tool preview)? A temporary brush is skipped by HammeriteSubtractiveCompiler, so a preview never carves the world before it is committed, and is drawn by an always-visible wireframe instead. Committing one (setting this false) has to re-route it into the normal display and recompile, so the change is a signal rather than a plain assignment.

layer_id

var layer_id: int = 0

The HammeriteLayer this brush is on. Changing it emits layer_changed and reorders the compiled program.

map

var map: HammeriteMap

The map this brush was added to, or null before HammeriteMap.add_brush() or once the map is freed. Held weakly: the map holds its brushes, and a brush holding the map as well is a cycle that keeps a map alive after the game has let go of it.

order

var order: int = 0

CSG evaluation order. HammeriteMap.rewire_objects() sorts brushes by this; ties preserve insertion order. All-zero (the default) preserves today's iteration order exactly.

order_sequence

var order_sequence: int = 0

Where this brush stands among brushes tied on order. 0 - the default, and what an authored brush wants - means "wherever my id puts me", so ties fall in the order they were made. A brush that stands in for an earlier one inherits the earlier one's place instead. Clipping a solid used to move both halves to the end of the program, behind the air brush carving it, and since a solid after an air brush FILLS it, the room in the clipped solid silently closed up.

prefab_id

var prefab_id: int = 0

The HammeritePrefab.prefab_id this brush was placed from, or 0 if it is not part of a prefab.

priority

var priority: int = 0

Point-in-cell tiebreak override. 0 = derive from volume via the compiler's bigger-volume-wins / lesser-id rule; non-zero explicitly overrides it (higher wins). Changing it emits tier_changed.

properties

var properties: Dictionary = {}

Authored values for properties contributed by addons, e.g. the acoustic character of a room (hammerite-audio declares those on air brushes; core neither knows nor cares what they mean). Keys are namespaced by their schema - audio/zone_type - so two addons cannot collide. See HammeriteBrushPropertySchema / HammeriteBrushPropertyRegistry. SPARSE, deliberately: only values the author actually changed are stored here. Everything else resolves to the registered default at read time, which keeps saved maps free of a wall of untouched defaults and lets an addon's defaults improve without a migration. Read through get_property() rather than indexing this directly, or an untouched brush answers null.

selected

var selected: bool

Editor selection state, not saved. Emits selection_changed.

temporary_transform

var temporary_transform: Transform3D

Temporary transform applied during transform tool operations. This allows preview without recalculating geometry. Not saved; identity when no tool is using it.

transparent_brush_material

var transparent_brush_material: ShaderMaterial

Instance alias of stock_transparent_brush_material(); nothing in Hammerite reads it.

Experimental: The renderer's, not part of the supported API.

volume_type

var volume_type: int = 0

Whether this brush carves air or adds solid in the subtractive world. Changing it re-runs the compiler (air now carves / solid now fills).

Method descriptions

add_face()

func add_face(face: HammeriteFace) -> void

Add face to faces, adopting it and listening to it as assigning the whole array does.

apply_lightmap_uniforms()

static func apply_lightmap_uniforms(mat: ShaderMaterial, face: HammeriteFace) -> void

Point mat at face's baked lighting: the atlas textures, the scale it was baked at, and the up-to-4 light slots. Split out of build_face_material() because a decal wants exactly this and none of the rest. A decal is the wall's own polygon cut to shape, coplanar with it and carrying its UV2, so it is lit by the wall's bake through the wall's uniforms - with its own picture as the colour. Pre-bake a null lightmap leaves the samplers unset, which is the provisional (unlit) material.

Experimental: The renderer's, not part of the supported API.

build_face_material()

static func build_face_material(face: HammeriteFace, ghosted: bool = false, is_water_entity: bool = false, entity: HammeriteEntity = null, fill: StringName = &"") -> ShaderMaterial

Build the per-face render ShaderMaterial: the brush shader (or the ghosted/transparent variant) with this face's HammeriteFace.texture as color, the face's lightmap texture bundle, lightmap scale, and the up-to-4 light-slot light_colors / light_modulation uniforms. Shared by generate_mesh() (one material per face) and HammeriteMap3D's compiled cell mesh (one material per texture/lightmap group), so both build an IDENTICAL material. A sky face gets the shared sky material instead. Pre-bake a null lightmap yields the provisional (unlit) material. ghosted / is_water_entity / entity carry the per-brush context; the cell path leaves them at their defaults and passes fill instead, which is what makes a pool's surface draw as water. Side effect: hands the material to HammeriteFace.set_material().

Experimental: The renderer's, not part of the supported API.

calculate_face_area()

func calculate_face_area(face: HammeriteFace) -> float

The area of face's polygon on this brush, in square world units.

clear_property()

func clear_property(prop_name: StringName) -> void

Drop this brush's override of prop_name, returning it to the registered default.

clip()

func clip(clipping_plane: Plane, clip_texture: Texture2D) -> HammeriteBrush[]

Clips the brush by the given plane. Always returns two elements, below the plane then above it, either of which can be null: - [below, above] - the plane divides the brush and both halves are solids. - [brush, null] or [null, brush] - the plane does not divide it. It misses the brush, grazes a corner, or lies along one of its own faces, so the whole brush is on one side and there is nothing on the other. - [null, null] - the plane divides it, but into something that is not a brush. Nothing here can be built, and the caller must leave the original alone: a brush is the convex volume its faces bound, and a face short of three vertices bounds nothing).

clip_epsilon()

static func clip_epsilon() -> float

How far off a plane a vertex must sit to count as being on one side of it, in world units. Below this the cut is thinner than anything the author could have meant, and the two "halves" it would make are a brush and a wafer.

compute_indices()

func compute_indices(num_vertices: int) -> PackedInt32Array

Triangle-fan indices for a convex polygon of num_vertices corners.

Experimental: The brush's corner cache, not part of the supported API.

compute_vertices()

func compute_vertices(face: HammeriteFace, ignore_cache: bool = false) -> Vector3[]

face's corners, from the cache unless the brush is dirty or ignore_cache. Writes what it works out back to the face's cache. Returns the cached array itself, not a copy.

Experimental: The brush's corner cache, not part of the supported API.

contains_point()

func contains_point(point: Vector3, epsilon: float = 0.001) -> bool

Whether point is strictly inside this brush, at least epsilon behind every face; a point on a face is outside.

copy()

func copy() -> HammeriteBrush

A deep copy: the faces are new objects, sharing nothing with this brush's.

copy_placement_to()

func copy_placement_to(other: HammeriteBrush) -> void

Everything that says where this brush BELONGS: its layer, its group, the prefab instance it is part of. Distinct from copy_tier_metadata_to(), which says what it IS. A brush split off another - clipped, extruded, edited into two by moving a vertex - has to land in the same layer and the same group as the one it came from, or it quietly falls out of both.

copy_tier_metadata_to()

func copy_tier_metadata_to(other: HammeriteBrush) -> void

Carry what this brush IS onto other: its volume tier, fill, detail flag, CSG ordering and extendable properties. Everything that makes a room a room rather than a box of planes. Public because a brush made FROM another has to be the same kind of thing, and the tools that make one do not all live in this addon - an extruded room that came out solid is a room that carves nothing and shows nothing.

csg_order_key()

func csg_order_key() -> int

This brush's place among ties on order: the sequence it inherited, or its own id.

cube()

static func cube(origin: Vector3, texture: Texture2D) -> HammeriteBrush

A six-faced axis-aligned brush of ZERO size at origin, every face textured texture: the seed the editor's brush tool drags out to size. Not a valid brush until its faces are moved apart (set_face_distance()); it has no id and no map. A box of a size is from_aabb().

Experimental: The drag tool's seed, not part of the supported API.

from_aabb()

static func from_aabb(box: AABB, texture_name: StringName = &"", volume: int = 0) -> HammeriteBrush

An axis-aligned box filling box, every face textured texture_name, adding solid or carving air as volume says. It has no id and no map; HammeriteMap.create_box_brush() gives it both. Faces in the order +Z, -Z, -X, +X, +Y, -Y.

generate_mesh()

func generate_mesh(for_collision: bool = false) -> Mesh

A new ArrayMesh of this brush in map space, one surface per face with its material built by build_face_material(). With for_collision every face is kept and no materials are built; otherwise faces the map hides are left out.

Experimental: The renderer's, not part of the supported API.

generate_outline_mesh()

func generate_outline_mesh() -> Mesh

A new line-strip ArrayMesh outlining every face, for the editor's wireframe.

Experimental: The renderer's, not part of the supported API.

get_aabb()

func get_aabb() -> AABB

World-space box enclosing this brush, held for as long as its vertices are - see compute_vertices(), whose cache this one lives and dies beside.

get_brush_entity()

func get_brush_entity() -> HammeriteBrushEntity

The owning HammeriteBrushEntity, or null for world brushwork, a brush in no map, or an owner the map does not hold.

get_chroma_bleed()

static func get_chroma_bleed() -> float

How far a surface takes the COLOUR of the light bouncing onto it: 0 keeps every wall its own colour, 1 recolours it entirely along the bleed. 0.2 is what the effect was hardcoded at. A render-time look, not a baked one - the bleed itself is in the atlas either way, and this only decides how much of it shows - so set_chroma_bleed() can move it on a map that is already standing, with no re-bake.

Experimental: The renderer's, not part of the supported API.

get_entity_property()

func get_entity_property(prop_name: StringName, default = null) -> Variant

prop_name of the owning entity, or default if it lacks one or the brush is world brushwork.

get_face_average()

func get_face_average() -> Vector3

The average of the face centroids, in map space: the middle of a box, but nearer the wide end of a wedge than its centre of volume is. Recomputes the corners first if they are stale.

get_face_centroid()

func get_face_centroid(face: HammeriteFace) -> Vector3

The middle of face, worked out from the planes every time - which is what a baked surface is keyed by (HammeriteMap.surface_key()), so it must not change. Without writing what it works out back onto the face: a carved fragment's corners are the ones the carve made, and replacing them with these, rotated and a unit in the last place off, left a level recompiled after an edit with walls a fresh compile of the same level does not have.

get_face_from_normal()

func get_face_from_normal(normal: Vector3) -> HammeriteFace

The first face whose normalized normal is approximately normal, or null. normal must itself be normalized.

get_property()

func get_property(prop_name: StringName, fallback = null) -> Variant

The effective value of extendable property prop_name: this brush's authored override if it has one, otherwise the default declared by whichever addon registered the property, otherwise fallback. Note the fallback is a last resort for an UNDECLARED key - it is what you get when the owning addon was never loaded, which in a shipped build means a missing dependency rather than a missing value. A debug build warns, once a key, when it is reached for.

get_sky_material()

static func get_sky_material() -> ShaderMaterial

The material a sky face wears while a sky renderer is standing, pointed at its render: set by the renderer as it starts, and back to null when a map has no sky to render.

Experimental: The renderer's, not part of the supported API.

get_surface_material()

static func get_surface_material() -> ShaderMaterial

What every opaque brush face is drawn with: walls, floors and ceilings, detail brushwork and brush entities. A spatial shader built on hammerite_surface.gdshaderinc, which writes one function, brush_surface(), and leaves the bake's light, the probes and the editor's views to Hammerite - a normal map, a detail texture, a glow of the game's own. Each face is drawn with a copy, and the uniforms Hammerite's own shader is given that this one leaves unset - the caustic and noise textures - are taken from stock_brush_material(). One for every map in the process: set it once, before a map is built. Null draws stock_brush_material(). Faces whose texture is alpha-masked, the sky and the surfaces of fills are drawn as before; so is a HammeritePropModel.

get_vertices()

func get_vertices() -> Vector3[]

Every face's corners concatenated, in map space - so a corner shared by three faces appears three times. A new array.

has_property_override()

func has_property_override(prop_name: StringName) -> bool

True when this brush carries an authored override for prop_name, as opposed to reading the registered default.

has_transparent_face()

func has_transparent_face() -> bool

Whether any face can be seen through. As detail, that decides what this brush hides.

inherit_csg_position_from()

func inherit_csg_position_from(source: HammeriteBrush) -> void

Stand where source stood in the CSG program. For a brush that replaces another - the halves of a clip - rather than one that is merely like it: a duplicate is a new brush and belongs where new brushes go, at the end.

intersect_segment()

func intersect_segment(from: Vector3, to: Vector3) -> PackedVector3Array

Check if a given segment intersects with the brush geometry. Returns [point, normal] for where the segment enters the brush, or an empty array if it misses (as Geometry3D.segment_intersects_convex()).

intersect_vertices()

func intersect_vertices(plane: Plane) -> Vector3[]

The corners of the polygon this brush's other faces cut out of plane, welded and sorted about its normal. Always works them out from the planes, ignoring the cache; empty or fewer than three when the plane does not bound the brush.

Experimental: The brush's corner cache, not part of the supported API.

intersects_brush()

func intersects_brush(brush: HammeriteBrush) -> bool

Whether brush overlaps this one. Brushes that only share a face do not count; for whether a point is inside, contains_point(). Uses the Separating Axis Theorem (SAT) to detect intersection between two convex brushes. Two convex objects intersect if there is no separating plane between them.

invalidate()

func invalidate() -> void

Mark the cached corners and bounds stale, so the next query works them out from the planes again. Does not notify anything; see request_recalculate_all() for that.

Experimental: The brush's corner cache, not part of the supported API.

is_convex()

func is_convex() -> bool

Check if the brush is convex or not. Verifies that each vertex is behind every plane. Uses a tolerance of 0.001 to account for floating point precision issues.

is_filled()

func is_filled() -> bool

Whether this brush is a room with something in it. False for solid, whatever its fill says, so callers do not have to check both.

is_filled_with()

func is_filled_with(fill_id: StringName) -> bool

True when this brush is filled with fill_id.

is_owned_by_entity()

func is_owned_by_entity() -> bool

Whether a HammeriteBrushEntity owns this brush (brush_entity_id is nonzero).

mirror()

func mirror(axis: int, coordinate: float) -> void

Mirror this brush through the plane perpendicular to axis at coordinate (Vector3.AXIS_X/Y/Z), in place. A brush IS its face planes, so mirroring one is mirroring each of them: the normal's component along the axis flips, and the plane's distance moves by twice what the mirror stands at. A reflection maps a solid onto a solid and its outward normals onto outward normals, so what comes out is the same brush seen the other way round, still convex, still wound correctly - no vertex is touched, because vertices are worked out from the planes. The texture goes with the geometry rather than being mirrored itself: a mirrored wall shows its texture the way round the wall now faces, which is what an author flipping a doorway wants.

move_face_to()

func move_face_to(face: HammeriteFace, distance: float) -> bool

Move face to distance, but only if this is still a brush afterwards. Returns whether it moved. A face dragged far enough stops meeting the faces around it - pushed past the opposite wall, or past the corner where two others cross - and a plane that no longer cuts a polygon out of the brush is not a face of anything. The brush that comes out has a side missing: it renders inside-out, picks unpredictably and compiles into a world with a hole in it, all from a drag that went one grid square too far. So the drag stops at the last place the brush was a brush, and says so by returning false, rather than being allowed to arrive somewhere there is no brush at all.

recompute_face_vertices()

func recompute_face_vertices() -> void

Work out every face's corners from the planes again and clear dirty. Emits nothing.

Experimental: The brush's corner cache, not part of the supported API.

request_delete()

func request_delete() -> void

Ask the owning map to remove this brush by emitting delete_requested. Does nothing before the brush is added to a map.

request_recalculate_all()

func request_recalculate_all() -> void

Recompute the corners now, then emit geometry_changed and face_attribute_changed, which the map and the brush's nodes answer by recompiling and remeshing. Call after changing face planes without going through their setters.

request_recalculate_collision()

func request_recalculate_collision(recompute_verts: bool = true) -> void

Emit geometry_changed, recomputing the corners first if recompute_verts.

request_recalculate_mesh()

func request_recalculate_mesh(recompute_verts: bool = true) -> void

Emit face_attribute_changed, recomputing the corners first if recompute_verts.

set_chroma_bleed()

static func set_chroma_bleed(value: float, map: HammeriteMap = null) -> void

Set get_chroma_bleed() and push it onto every material map has already built, so the knob moves the world under the author rather than the next thing they build.

Experimental: The renderer's, not part of the supported API.

set_face_distance()

func set_face_distance(face_idx: int, dist: float) -> void

Move face face_idx along its normal to plane distance dist, emitting as the plane setter does. Where it could leave no brush - a drag, say - use move_face_to(), which refuses that; this is for building one face by face, as cube()'s seed is dragged out.

set_property()

func set_property(prop_name: StringName, value) -> void

Author extendable property prop_name. Setting a property back to its registered default ERASES the override rather than storing it, so the sparse invariant holds however the author gets there - typing the default in by hand is the same as never touching it. Emits property_changed with the effective value either way. Properties are metadata, not geometry: this does NOT invalidate the compiled worldrep. A consumer that needs to react (the audio graph) listens for HammeriteMap.brush_property_changed.

set_sky_material()

static func set_sky_material(value: ShaderMaterial) -> void

The material a sky face wears while a sky renderer is standing, pointed at its render: set by the renderer as it starts, and back to null when a map has no sky to render.

Experimental: The renderer's, not part of the supported API.

set_surface_material()

static func set_surface_material(value: ShaderMaterial) -> void

What every opaque brush face is drawn with: walls, floors and ceilings, detail brushwork and brush entities. A spatial shader built on hammerite_surface.gdshaderinc, which writes one function, brush_surface(), and leaves the bake's light, the probes and the editor's views to Hammerite - a normal map, a detail texture, a glow of the game's own. Each face is drawn with a copy, and the uniforms Hammerite's own shader is given that this one leaves unset - the caustic and noise textures - are taken from stock_brush_material(). One for every map in the process: set it once, before a map is built. Null draws stock_brush_material(). Faces whose texture is alpha-masked, the sky and the surfaces of fills are drawn as before; so is a HammeritePropModel.

stock_brush_material()

static func stock_brush_material() -> ShaderMaterial

The shared opaque brush material that build_face_material() duplicates per face.

Experimental: The renderer's, not part of the supported API.

stock_ghosted_brush_material()

static func stock_ghosted_brush_material() -> ShaderMaterial

The shared material for ghosted brushes.

Experimental: The renderer's, not part of the supported API.

stock_sky_material()

static func stock_sky_material() -> ShaderMaterial

The flat material a sky face wears when no sky renderer is standing.

Experimental: The renderer's, not part of the supported API.

stock_transparent_brush_material()

static func stock_transparent_brush_material() -> ShaderMaterial

The shared material for alpha-masked textures and water.

Experimental: The renderer's, not part of the supported API.

subtract_brush()

func subtract_brush(brush: HammeriteBrush) -> HammeriteBrush[]

Subtracts brush from this brush, leaving this one untouched. Returns the new convex pieces that remain, which carry this brush's group and layer but no id and no map. The cut faces wear the textures of brush's faces.

validate_geometry()

func validate_geometry() -> bool

Whether this is a real brush: at least four faces, each with at least three corners. Caches the corners as a side effect, and clears dirty when it returns true.

validate_geometry_of()

static func validate_geometry_of(brushes: HammeriteBrush[]) -> PackedByteArray

validate_geometry() of each of brushes, whether each is valid, in order. The native kernel works out the corners of the ones that are dirty - a map's brushes all are, as it loads.

Experimental: The brush's corner cache, not part of the supported API.

vertex_weld_epsilon()

static func vertex_weld_epsilon() -> float

How far apart two corners computed for the same point may land, and still be that one point. Where more than three planes meet - a pyramid's apex - each triple of them puts the corner somewhere slightly different, and at a few hundred units from the origin that difference is thousandths. A relative comparison left eight copies of one apex on each side of a pyramid, and a turned one lost its apex to the inside test altogether, and with it the brush.

Hammerite 2.0 · built from 6d57ab2d, 2026-10-17