Flyweight

Intent: Share a single copy of expensive immutable data (mesh, texture, stats) across many instances so memory isn’t duplicated.

Acron Mapping

Pattern role

Acron equivalent

Shared intrinsic state

NodePath loaded once via loader.loadModel() / ModelPool

Instance-specific state

Each instance’s position, color, scale set via NodePath methods

Instancing

.instanceTo(parent) — renders the same geometry at multiple transforms with one draw call

How to Implement

  1. Load the shared model once: shared = loader.loadModel('tree.bam'). ModelPool caches it automatically on repeated loadModel() calls with the same path.

  2. For each instance call instance = shared.instanceTo(render) — this creates a new NodePath that references the same underlying geometry without copying it.

  3. Set per-instance state on the returned NodePath: instance.setPos(x, y, z), instance.setScale(s), instance.setColorScale(r, g, b, 1).

  4. Store instance-specific logical data (health, team, etc.) in a parallel Python dict keyed by NodePath id, not on the node itself.

Code Sketch (Python)

import random

# Load geometry once — ModelPool prevents duplicate loading
tree_model = loader.loadModel('assets/tree.bam')
tree_model.setScale(1)  # normalise once on the shared model

forest = []
for _ in range(1000):
    instance = tree_model.instanceTo(render)
    instance.setPos(
        random.uniform(-200, 200),
        random.uniform(-200, 200),
        0
    )
    # per-instance tint — does NOT copy geometry
    instance.setColorScale(
        random.uniform(0.8, 1.0),
        random.uniform(0.8, 1.0),
        random.uniform(0.8, 1.0),
        1
    )
    forest.append(instance)

Pitfalls

  • Don’t use copyTo() when you want flyweight — it deep-copies geometry into a new node, defeating the purpose. Use instanceTo() for shared geometry.

  • Render attribute inheritance: attributes set on tree_model affect all instances. Set shared visual state (LOD, shader) on the parent; set per-instance variation on the instance NodePath.

  • Removing instances: call instance.detachNode() (not removeNode() on the shared model) to destroy one instance while leaving others alive.