Skip to content

renderer

FreeBodyEngine.graphics.renderer #

Call(mesh, transform, material, camera) dataclass #

One queued draw, recorded via Renderer.submit() and issued later by Renderer.flush() rather than immediately - this indirection is what lets flush() sort/group calls (by blend mode for correct transparency, and by material/mesh to cut redundant state changes) instead of drawing in whatever order the scene tree happened to be walked.

blend_mode property #

camera instance-attribute #

material instance-attribute #

mesh instance-attribute #

transform instance-attribute #

camera_distance() #

Squared distance from the camera to this call's world position - used to sort transparent calls back-to-front. Squared (not sqrt'd) since only the relative order matters, not the actual distance.

Renderer() #

Bases: Service

Abstract base class for a graphics backend (GL33Renderer, GL44Renderer, DummyRenderer, ...). Registers itself as the 'renderer' service and defines the low-level resource-creation/drawing API a GraphicsPipeline drives to actually produce a frame; concrete subclasses fill in every @abstractmethod below for their own API (OpenGL, ...).

Registers as the 'renderer' service and creates the shared texture manager.

calls = [] instance-attribute #

create_framebuffer property #

Returns this backend's Framebuffer subclass (the class itself, not an instance - callers construct it themselves, passing their own width/height/attachments).

texture_manager = TextureManager() instance-attribute #

clear(color) #

Clears the currently bound framebuffer's color (and depth) buffers to color.

clear_scissor() #

Undoes set_scissor(), restoring unclipped drawing. See set_scissor()'s docstring for why this defaults to a no-op.

create_buffer(data) #

Creates a backend-specific Buffer already initialized with data.

destroy() #

Releases the backend's GPU context and resources.

disable_depth_testing() #

Disables GL_DEPTH_TEST (GL33Renderer/GL44Renderer). UIRenderer calls this before drawing every frame - see its own comment for why a 2D UI overlay can't share the 3D pipeline's depth-test state.

draw_circle(radius, position, color) #

Draws a filled circle at the position.

:param start: The center of the circle (NDC). :type start: tuple[float, float] :param radius: The radius of the circle (NDC). :type radius: float

draw_line(start, end, width, color) #

Draws a line between the first and second point.

:param start: The starting point. :type start: tuple[float, float] :param end: The end point. :type end: tuple[float, float] :param width: The thickness of the line. :type width: float

draw_mesh(mesh, material) #

Draws mesh once, using material's currently bound shader and uniforms.

draw_mesh_instanced(mesh, material, model_matrices, camera) #

Draws len(model_matrices) copies of mesh in a single GPU instanced draw call, one per row of model_matrices (shape (N, 4, 4)), using material. Not currently wired into flush()'s automatic batching - see graphics/instancing.py's module docstring for why. Real infrastructure for a caller that wants to submit genuine GPU instancing explicitly.

draw_model(model, transform, camera) #

Draws every sub-mesh of model, setting each one's mapped material's model/view/proj uniforms from transform/camera before delegating to draw_mesh() - the one piece of multi-mesh draw logic shared by every backend, so it lives here instead of being reimplemented per-renderer.

enable_depth_testing() #

Enables GL_DEPTH_TEST (GL33Renderer/GL44Renderer). Concrete no-op default so calling this is always safe regardless of backend, matching set_scissor()'s reasoning.

flush_opaque() #

Draws and dequeues every OPAQUE/ADDITIVE Call currently queued (leaving any TRANSPARENT ones queued - see flush_transparent()), grouped by (material, mesh) - via graphics.instancing. group_by_state() - purely to cut redundant glUseProgram/uniform/ texture-bind churn between consecutive draws that share a material (state-minimizing batching, not GPU instancing - see graphics/instancing.py's module docstring for why real per-instance-transform instancing isn't part of this). Drawn with depth testing/writing on; ADDITIVE still writes depth (e.g. a glow effect should still occlude/be occluded) while blending additively, unlike TRANSPARENT.

Split from flush_transparent() (rather than one combined flush()) because a deferred pipeline needs a framebuffer rebind between the two - see PBRPipeline.draw(): opaque calls render into the multi-attachment G-buffer, and only after that pipeline's own lighting composite pass runs do transparent calls get drawn, forward-shaded onto the composite's single 'lit' output.

flush_transparent() #

Draws and dequeues every TRANSPARENT Call currently queued, individually (no state-minimizing grouping - see flush_opaque()), sorted back-to-front by camera distance (the only order that composites correctly without per-pixel order-independent blending), with alpha blending on and depth testing on but depth writing off, so transparent objects don't occlude each other incorrectly or block opaque geometry drawn earlier.

get_image_class() #

Returns this backend's Image subclass.

get_max_buffer_size() #

Returns the largest size, in bytes, this backend's buffer type can hold.

get_mesh_class() #

Returns this backend's Mesh subclass, for code that needs to construct a mesh generically (e.g. mesh.create_static_mesh()).

load_image(texture) #

Uploads texture to the GPU and returns a backend-specific Image wrapping it.

load_image_from_atlas(data) #

Loads an Image from a pre-baked texture atlas entry.

load_material(data) #

Builds a backend-specific Material from data (e.g. a parsed .fbmat file).

load_shader(vertex, fragment, injector=Injector, geometry=None) #

Compiles vertex/fragment (and optional geometry) FBUSL source into a backend-specific Shader, using injector to resolve engine-provided builtins.

on_destroy() #

Unsubscribes resize() from the FRAMEBUFFER_RESIZE event.

on_initialize() #

Subscribes resize() to the FRAMEBUFFER_RESIZE event, so every backend's viewport/surface stays in sync with the window without wiring this up itself.

resize(size) #

Updates the backend's viewport/surface to match the new framebuffer size (pixels). Called automatically on FRAMEBUFFER_RESIZE - see on_initialize().

set_blend_mode(mode) #

Configures blending for subsequent draws: OPAQUE disables blending entirely (and enables depth writes), TRANSPARENT enables standard alpha blending with depth writes off (but depth testing still on), ADDITIVE enables additive blending with depth writes on. Called by flush() between groups, so a backend only needs to change actual GL state when mode differs from what's already active.

set_scissor(x, y, width, height) #

Restricts drawing to the (x, y, width, height) rectangle - top- left origin, Y-down pixels, matching ui/renderer.py's own screen- space convention - until the next set_scissor()/clear_scissor(). Concrete no-op default: a backend that doesn't override this (e.g. DummyRenderer) just draws unclipped rather than raising, since unclipped drawing is a correctness-preserving fallback (UIRenderer's scroll-container clipping is a visual nicety, not something other engine code depends on) - GL33Renderer/GL44Renderer override it with a real glScissor.

submit(mesh, material, transform, camera) #

Queues a draw instead of issuing it immediately - see flush_opaque()/flush_transparent().