The game needs a large and growing library of objects, and per docs/RECIPES.md the
core mechanic is combinatorial: a Horse plus a filled Wings slot becomes a Pegasus;
a humanoid torso plus a horse body becomes a Centaur; gear (hats, weapons, shirts) attaches to
named sockets on a creature. Whatever sourcing method is chosen has to serve that shape of
problem, not just "get some 3D models." Three approaches were actually tried, not just reasoned about
in the abstract.
Build every object as a small scene tree of Godot primitives — BoxMesh,
CylinderMesh, SphereMesh, CapsuleMesh,
TorusMesh, PrismMesh — each a MeshInstance3D with a flat
StandardMaterial3D (solid albedo colour, shading_mode = Unshaded or a
single hard directional light with no smoothing group). CSG nodes
(CSGBox3D, CSGCombiner3D, boolean unions) are useful for one-off shape
sculpting but bake to static meshes for runtime — plain MeshInstance3D composition is
lighter and was used for everything actually shipped in the POC.
Tested: every creature and gear scene in pocs/object-gallery/ is
built this way — e.g. the cute basic creature is two SphereMeshes (a body ball and a
head ball) plus four stubby CapsuleMesh legs, all children of one Node3D
root. Attachment points are plain Marker3D nodes named socket_head,
socket_right_hand, socket_back, etc., placed by eye in the editor.
| Fit for combine/attach mechanic | Excellent — a creature is a scene tree of parts; adding a Marker3D socket is a one-line editor operation, and gear scenes reparent onto a socket with zero import friction, because everything already lives in the same engine, same units, same material system. |
| Iteration speed | Fast — no external tool round-trip; tweak a primitive's size/position in the Godot inspector and see it live. |
| Visual fit | Good — flat-shaded primitives naturally produce the "low vertex count, clearly recognisable, not photoreal" look the brief asks for. |
| Scaling to "many objects over time" | Good with discipline — fine by hand up to dozens of objects; past that, a small in-house primitive-kit (a library of pre-styled part scenes: "stubby leg", "round head", "pointed hat") keeps it from becoming repetitive boilerplate. This is a process risk, not a technical one. |
| Complex organic shapes (dragons, faces) | Limited — primitives alone struggle with anything that needs a genuinely sculpted silhouette. Acceptable for a stylised game; would need a heavier pipeline (Approach C, or an actual DCC tool) for hero assets later. |
The brief predicted direct egress to kenney.nl / poly.pizza /
quaternius.com would be blocked by the container's allowlist. That
prediction was wrong — this container's egress allowlist is more permissive than
expected, and direct requests to all three domains returned HTTP 200. So in this
specific environment, the "GitHub mirror" workaround wasn't strictly necessary. It's still worth
documenting, because (a) allowlists can and do change between environments/deployments, and (b)
GitHub mirrors are frequently better-organised for engine use than the original download pages.
Tested: a GitHub code search for repos mirroring Kenney and Quaternius packs
turned up several actively-used, already-Godot-adapted mirrors, e.g.
kirbycope/godot-quaternius (a whole Godot project pre-wired with Quaternius'
Fantasy Props, Modular SciFi, Universal Base Characters, etc. packs, each with its own
_README.md crediting the source) and Calinou/kenney-* (individual Kenney
packs repackaged "for quick use in Godot", maintained by a Godot core contributor). Repo contents
were confirmed reachable and non-empty via the GitHub API without needing a full clone.
| Licensing | CC0 for both source libraries — Kenney and Quaternius both publish under CC0 (public domain equivalent): no attribution legally required, no share-alike, no revenue-share, safe for a commercial Christmas 2026 release. This matters a lot more than it sounds — anything CC-BY or "free for non-commercial" would need per-asset attribution tracking or would be an outright blocker for a paid/monetised release, which doesn't scale once you have hundreds of props. |
| Fit for combine/attach mechanic | Weak-to-moderate — these packs are pre-modelled, complete objects (a finished sword, a finished barrel), not part-libraries designed for socketed recombination. Useful as whole props or as a source of individual meshes to strip for parts, not as the compositional backbone. |
| Visual consistency | Needs curation — different packs have different scales, poly budgets, and colour palettes; mixing them into one game requires a pass to reconcile scale/material style, which is exactly the kind of hidden cost "just download some assets" glosses over. |
| Best use here | One-off environment dressing and props that aren't part of the creature/gear recombination system — barrels, crates, rocks, foliage, furniture for the casting-table room. Not creatures, not socketed gear. |
trimesh installed cleanly via pip and was used to build a real test asset: a small
creature (a box body, an icosphere head, four cylinder legs) composed with translations and
exported to a valid, correctly-sized .glb (~9.7 KB) in a few lines of Python.
It works, and it's a legitimate way to batch-generate or algorithmically vary meshes outside the
engine.
body = trimesh.creation.box(extents=[1.0, 0.6, 0.5])
head = trimesh.creation.icosphere(subdivisions=1, radius=0.3)
head.apply_translation([0.65, 0, 0.15])
legs = [leg.copy().apply_translation([x, y, -0.4]) for x in (-.35,.35) for y in (-.2,.2)]
trimesh.Scene([body, head] + legs).export('creature.glb') # imports straight into Godot
| Fit for combine/attach mechanic | Possible, but redundant — you'd still need Godot-side socket metadata (Marker3D nodes) on the imported result, so you gain nothing over building directly in Godot for anything that's fundamentally "boxes and spheres in a hierarchy" — which is most of this game's objects. |
| Where it actually wins | Batch/parametric generation — e.g. programmatically generating a whole family of variant meshes (say, 64 slightly different crystal shapes keyed off hexagram data) as a build step, then importing the .glb files. That's a real future use case for the recipe/element system, just not needed for the object-gallery POC. |
| Iteration speed | Slower — edit Python → re-run → re-export → re-import into Godot → reimport asset, versus tweak-and-see in the Godot editor directly. |
bpy (Blender as a pip module) | Assessed, not installed — bpy gives full Blender modelling/sculpting/UV/rigging power from Python, which is real headroom for later hero assets or rigged animation, but it's a heavy dependency (hundreds of MB, its own Python ABI constraints) for what is, right now, a low-poly primitive-composition game. Worth revisiting if the game grows organic/sculpted creatures or needs skeletal animation baked outside the engine — not worth installing for this POC. |
RECIPES.md: a creature is already, conceptually, a set of slot-filling parts —
building it as a literal Godot scene tree of parts with named Marker3D sockets makes the engine
representation identical to the game-design representation. CC0 GitHub-mirrored assets are
a good supplementary source for one-off, non-recombining props (environment dressing), not for the
creature/gear system itself. The Python/trimesh pipeline is a legitimate future tool for
parametric batch-generation (e.g. generating element-keyed crystal variants at build time) but adds
an unnecessary round-trip for the hand-authored, in-editor part composition this POC needed.
What got discarded and why, briefly:
bpy/Blender for this POC — discarded for now on cost/benefit: heavy dependency, no current need for sculpting or rigging that primitives can't cover.MeshInstance3D hierarchies were used throughout the POC since they're lighter at runtime and sockets attach identically either way.
This question matters because it drives how every future object gets built, not just this POC's
eight creatures. Three real Godot techniques exist, and the game actually needs a mix of them —
matching the intuition that some cases morph and some don't. RECIPES.md itself already
contains an example of each need without naming the techniques: Horse→Pegasus (same skeleton, add
wings), Centaur (two different creatures literally glued together), and Simple Life (an arbitrary
freeform blob-creature from unconstrained slots).
Godot's MeshInstance3D supports per-vertex blend shapes (set up in a DCC tool or
via ArrayMesh surface morph arrays), interpolated at runtime via
set_blend_shape_value(). Requires identical topology/vertex count between the base
mesh and every morph target — it deforms one mesh, it does not attach new geometry.
Separate scenes/meshes parented onto named Marker3D/BoneAttachment3D
sockets on a base skeleton. No topology constraint between parts — a horse body and a humanoid
torso don't need to agree on vertex count, they just need to agree on a socket transform. This
is what the object-gallery POC uses throughout.
Generate geometry from parameters at runtime or at creation-time — e.g.
CSGCombiner3D unions of primitives whose size/position come from formula inputs, or
Godot's GPUParticles-adjacent metaball-like blending for soft, mushed-together
shapes. No fixed part library; the shape itself is computed.
| Game need (from RECIPES.md) | Technique | Why |
|---|---|---|
| Horse → Pegasus (same body, wings slot filled) | Modular attachment (not blend shapes, despite looking like a "growth") | The brief frames this as a morph, but implementation-wise it's cleaner as attachment: wings are a separate scene socketed at socket_back and toggled on/off. A true blend-shape approach would need the wingless horse mesh to already contain (zeroed) wing geometry, which conflicts with "the Wings slot is empty" being a real, inspectable game state. Attachment also lets wing type vary (64 possible wing elements per RECIPES.md) by swapping the attached scene — a blend shape can't swap identity, only interpolate one fixed target. |
| Centaur (humanoid torso + horse body) | Modular attachment | Textbook case for (b) — two independently-authored parts (the POC's humanoid torso, the POC's horse hindquarters) glued at one socket. Robust, and exactly how the POC's Centaur scene is built. |
| Gear (hats, weapons, shirts, shoes) | Modular attachment | No ambiguity — gear is discrete, ownable, tradeable inventory per the docs (Ingredients/Blueprints tabs), so it must be a separable object, not a deformation of the wearer. |
| Simple Life (2 balls mushed + stubby limbs, arbitrary weird shapes from free Body/Colour slots) | Procedural/metaball-style, built from a small primitive palette | This is explicitly the "no fixed pattern" case — 64×64 discoverable combinations per RECIPES.md. A parameterised generator (pick N body-blob primitives, scale/position/colour them from the filled element's trigram data) scales to that combinatorial space without hand-authoring each result. The POC's "cute basic creature" is a hand-built instance of what this generator would produce — useful as the concrete reference shape. |
| Same-creature colour/size/texture variation (e.g. every horse recoloured per its Colour/Texture slot) | Not geometry at all — material parameters | Swapping albedo_color on a shared StandardMaterial3D (or a shader uniform) covers this without touching mesh data or needing blend shapes. |
| Future: rigged animation (walk cycles, attack poses) if/when the game needs them | Skeleton3D + attachment, not covered by this POC | Out of scope here (the POC is static display), but noted because it constrains future part design: parts meant to attach to an animated base eventually need to attach via BoneAttachment3D rather than a static Marker3D, so it's worth using Godot's skeleton-attachment nodes once animation is real, rather than static markers everywhere. |
The three real needs in the design map to three different techniques, and no single technique covers all of them — a hybrid is correct, not a compromise. Concretely, for the object library going forward:
Marker3D
sockets + reparenting attached scenes. This is the backbone; it's what pocs/object-gallery/
implements for all eight creatures and seven gear pieces.