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
| Type | Name | Default |
|---|---|---|
int | id | 0 |
int | group_id | 0 |
int | prefab_id | 0 |
int | instance_id | 0 |
int | layer_id | 0 |
int | brush_entity_id | |
HammeriteFace[] | faces | |
bool | hidden | false |
bool | ghosted | false |
int | volume_type | 0 |
bool | is_detail | false |
int | order | 0 |
int | order_sequence | 0 |
int | priority | 0 |
StringName | fill | &"" |
int | interior_exterior | 0 |
Dictionary | properties | {} |
bool | is_temporary | false |
bool | selected | false |
bool | hovered | false |
bool | dirty | true |
Transform3D | temporary_transform | Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) |
HammeriteMap | map | |
ShaderMaterial | brush_material | <Object> |
ShaderMaterial | ghosted_brush_material | <Object> |
ShaderMaterial | transparent_brush_material | <Object> |
ShaderMaterial | surface_material | <Object> |
float | chroma_bleed | 0.2 |
bool | native_corners | <unknown> |
Methods
| Returns | Method |
|---|---|
HammeriteBrush | from_aabb(box: AABB, texture_name: StringName = &"", volume: int = 0) static |
HammeriteBrush | cube(origin: Vector3, texture: Texture2D) static |
void | invalidate() |
void | request_recalculate_all() |
void | request_recalculate_mesh(recompute_verts: bool = true) |
void | request_recalculate_collision(recompute_verts: bool = true) |
void | request_delete() |
HammeriteMap | get_map() |
void | set_map(new_map: HammeriteMap) |
int | get_layer_id() |
void | set_layer_id(new_layer: int) |
bool | is_owned_by_entity() |
int | get_brush_entity_id() |
HammeriteBrushEntity | get_brush_entity() |
void | set_brush_entity_id(new_id: int) |
Variant | get_entity_property(prop_name: StringName, default = null) |
Variant | get_property(prop_name: StringName, fallback = null) |
bool | has_property_override(prop_name: StringName) |
void | set_property(prop_name: StringName, value) |
void | clear_property(prop_name: StringName) |
void | add_face(face: HammeriteFace) |
void | set_face_distance(face_idx: int, dist: float) |
HammeriteFace | get_face_from_normal(normal: Vector3) |
HammeriteBrush | copy() |
void | copy_placement_to(other: HammeriteBrush) |
void | copy_tier_metadata_to(other: HammeriteBrush) |
int | csg_order_key() |
void | inherit_csg_position_from(source: HammeriteBrush) |
bool | is_filled() |
bool | is_filled_with(fill_id: StringName) |
void | mirror(axis: int, coordinate: float) |
float | calculate_face_area(face: HammeriteFace) |
bool | is_convex() |
PackedVector3Array | intersect_segment(from: Vector3, to: Vector3) |
HammeriteBrush[] | clip(clipping_plane: Plane, clip_texture: Texture2D) |
Vector3[] | get_vertices() |
AABB | get_aabb() |
void | set_chroma_bleed(value: float, map: HammeriteMap = null) static |
bool | has_transparent_face() |
ShaderMaterial | build_face_material(face: HammeriteFace, ghosted: bool = false, is_water_entity: bool = false, entity: HammeriteEntity = null, fill: StringName = &"") static |
void | apply_lightmap_uniforms(mat: ShaderMaterial, face: HammeriteFace) static |
Mesh | generate_mesh(for_collision: bool = false) |
Mesh | generate_outline_mesh() |
Vector3 | get_face_average() |
PackedInt32Array | compute_indices(num_vertices: int) |
Vector3[] | intersect_vertices(plane: Plane) |
Vector3 | get_face_centroid(face: HammeriteFace) |
Vector3[] | compute_vertices(face: HammeriteFace, ignore_cache: bool = false) |
void | recompute_face_vertices() |
bool | move_face_to(face: HammeriteFace, distance: float) |
bool | validate_geometry() |
PackedByteArray | validate_geometry_of(brushes: HammeriteBrush[]) static |
bool | intersects_brush(brush: HammeriteBrush) |
bool | contains_point(point: Vector3, epsilon: float = 0.001) |
HammeriteBrush[] | subtract_brush(brush: HammeriteBrush) |
Signals
delete_requested
signal delete_requested()
Emitted when brush has requested its deletion.
hidden_changed
signal hidden_changed(hidden: bool)
Emitted when hidden changes, with the new value: true when the brush has been hidden.
ghosted_changed
signal ghosted_changed(ghosted: bool)
Emitted when brush ghosted state has changed.
face_attribute_changed
signal face_attribute_changed()
Emitted when face attribute has changed.
temporary_changed
signal temporary_changed(is_temporary: bool)
Emitted when the brush stops (or starts) being a tool preview - see is_temporary.
geometry_changed
signal geometry_changed()
Emitted when brush geometry has changed.
layer_changed
signal layer_changed(old_layer_id: int, new_layer_id: int)
Emitted when brush layer has changed.
entity_id_changed
signal entity_id_changed()
Emitted when brush entity id has changed.
selection_changed
signal selection_changed(selected: bool)
Emitted when brush selection has changed.
hover_changed
signal hover_changed(hovered: bool)
Emitted when brush hover state has changed.
temporary_transform_changed
signal temporary_transform_changed(transform: Transform3D)
Emitted when temporary transform has changed (used during transform tool operations).
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.
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
| Value | Name | Description |
|---|---|---|
0 | SOLID | Fills its volume back in with rock. |
1 | AIR | Carves its volume out of the rock: a room, or part of one. |
enum InteriorExterior
| Value | Name | Description |
|---|---|---|
0 | AUTO | Worked out by the compiler: a room reaches the sky if it can see a sky face through its openings. |
1 | INTERIOR | Indoors whatever the compiler finds. |
2 | EXTERIOR | Reaching the sky whatever the compiler finds. |
Constants
BRUSH_MATERIAL
const BRUSH_MATERIAL = <Object>
The shared opaque brush material that build_face_material() duplicates per face.
Experimental: The renderer's, not part of the supported API.
GHOSTED_BRUSH_MATERIAL
const GHOSTED_BRUSH_MATERIAL = <Object>
The shared material for ghosted brushes.
Experimental: The renderer's, not part of the supported API.
TRANSPARENT_BRUSH_MATERIAL
const TRANSPARENT_BRUSH_MATERIAL = <Object>
The shared material for alpha-masked textures and water.
Experimental: The renderer's, not part of the supported API.
CLIP_EPSILON
const CLIP_EPSILON = 0.001
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.
SKY_MATERIAL
const SKY_MATERIAL = <Object>
The flat material a sky face wears when no HammeriteSkyRenderer is standing.
Experimental: The renderer's, not part of the supported API.
VERTEX_WELD_EPSILON
const VERTEX_WELD_EPSILON = 0.01
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.
NATIVE_BRUSHES
const NATIVE_BRUSHES = &"HammeriteNativeBrushes"
The class name of the optional native kernel behind native_corners.
Experimental: The brush's corner cache, not part of the supported API.
Property descriptions
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().
group_id
var group_id: int = 0
The editor group this brush is selected together with, or 0 for none.
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.
instance_id
var instance_id: int = 0
The HammeritePrefabInstance.instance_id this brush belongs to, or 0 if none.
layer_id
var layer_id: int = 0
The HammeriteLayer this brush is on. Changing it emits layer_changed and reorders the compiled program.
brush_entity_id
var brush_entity_id: int
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.
faces
var faces: HammeriteFace[]
The bounding faces. Assigning the array adopts each face (HammeriteFace.set_brush()) and listens to it; appending to it in place does neither, so assign a whole new array or use add_face().
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.
ghosted
var ghosted: bool = false
Is this brush ghosted (rendered transparently for reference). Takes precedence over normal rendering but not over hidden.
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).
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.
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.
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.
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.
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.
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.
is_temporary
var is_temporary: bool = false
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.
selected
var selected: bool = false
Editor selection state, not saved. Emits selection_changed.
hovered
var hovered: bool = false
Editor hover state, not saved. Emits hover_changed.
dirty
var dirty: bool = true
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.
temporary_transform
var temporary_transform: Transform3D = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
Temporary transform applied during transform tool operations. This allows preview without recalculating geometry. Not saved; identity when no tool is using it.
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.
brush_material
var brush_material: ShaderMaterial = <Object>
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.
ghosted_brush_material
var ghosted_brush_material: ShaderMaterial = <Object>
Instance alias of GHOSTED_BRUSH_MATERIAL; nothing in Hammerite reads it.
Experimental: The renderer's, not part of the supported API.
transparent_brush_material
var transparent_brush_material: ShaderMaterial = <Object>
Instance alias of TRANSPARENT_BRUSH_MATERIAL; nothing in Hammerite reads it.
Experimental: The renderer's, not part of the supported API.
surface_material
var surface_material: ShaderMaterial = <Object>
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 BRUSH_MATERIAL. One for every map in the process: set it once, before a map is built. Null draws BRUSH_MATERIAL. Faces whose texture is alpha-masked, the sky and the surfaces of fills are drawn as before; so is a HammeritePropModel.
chroma_bleed
var chroma_bleed: float = 0.2
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.
native_corners
var native_corners: bool = <unknown>
Whether validate_geometry_of() has the native kernel work out corners. Settable, so a suite can load both ways.
Experimental: The brush's corner cache, not part of the supported API.
Method descriptions
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.
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.
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.
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_mesh()
func request_recalculate_mesh(recompute_verts: bool = true) -> void
Emit face_attribute_changed, recomputing the corners first if recompute_verts.
request_recalculate_collision()
func request_recalculate_collision(recompute_verts: bool = true) -> void
Emit geometry_changed, recomputing the corners first if recompute_verts.
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.
get_map()
func get_map() -> HammeriteMap
See map.
set_map()
func set_map(new_map: HammeriteMap) -> void
Set by HammeriteMap.add_brush(). Only records the back-reference; it does not add the brush to new_map.
get_layer_id()
func get_layer_id() -> int
Getter for layer_id.
set_layer_id()
func set_layer_id(new_layer: int) -> void
Setter for layer_id; emits layer_changed only when the value changes.
is_owned_by_entity()
func is_owned_by_entity() -> bool
Whether a HammeriteBrushEntity owns this brush (brush_entity_id is nonzero).
get_brush_entity_id()
func get_brush_entity_id() -> int
Getter for brush_entity_id.
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.
set_brush_entity_id()
func set_brush_entity_id(new_id: int) -> void
Setter for brush_entity_id; emits entity_id_changed only when the value changes.
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_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.
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.
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.
clear_property()
func clear_property(prop_name: StringName) -> void
Drop this brush's override of prop_name, returning it to the registered default.
add_face()
func add_face(face: HammeriteFace) -> void
Add face to faces, adopting it and listening to it as assigning the whole array does.
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.
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.
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.
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.
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.
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.
calculate_face_area()
func calculate_face_area(face: HammeriteFace) -> float
The area of face's polygon on this brush, in square world units.
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.
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()).
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).
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.
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.
set_chroma_bleed()
static func set_chroma_bleed(value: float, map: HammeriteMap = null) -> void
Set 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.
has_transparent_face()
func has_transparent_face() -> bool
Whether any face can be seen through. As detail, that decides what this brush hides.
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.
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.
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_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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.