model
FreeBodyEngine.core.files.loaders.model
#
MODEL_MANIFEST_KEY = '_ENGINE_model_manifest.json'
module-attribute
#
GLBParser(data_bytes)
#
Splits a .glb (binary glTF) blob into its JSON and binary chunks,
per the glTF 2.0 binary container spec - the JSON chunk always comes
first and is required, the BIN chunk is optional and, when present,
always second.
Parses data_bytes immediately - raises ValueError if it isn't a
valid, version-2 .glb container, or if its declared length
exceeds the actual data.
GLTFParser(gltf_dict, bin_data, base_path=None)
#
Builds a Model from a parsed glTF JSON document (gltf_dict) plus
its binary buffer data - works for both .gltf (with bin_data either
empty or decoded from a data: URI) and .glb (with bin_data from
GLBParser.get_binary_buffer()). base_path is the directory the source
file lives in, needed to resolve any uri that points at an external
sibling file (a separate .bin or image) rather than embedding its data
inline.
Stores the parsed inputs; does no work itself - see the class docstring.
COMPONENT_FORMAT = {5120: 'b', 5121: 'B', 5122: 'h', 5123: 'H', 5125: 'I', 5126: 'f'}
class-attribute
instance-attribute
#
NUMPY_DTYPE = {5120: np.int8, 5121: np.uint8, 5122: np.int16, 5123: np.uint16, 5125: np.uint32, 5126: np.float32}
class-attribute
instance-attribute
#
NUM_COMPONENTS = {'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, 'VEC4': 4, 'MAT2': 4, 'MAT3': 9, 'MAT4': 16}
class-attribute
instance-attribute
#
base_path = base_path
instance-attribute
#
bin_data = bin_data or b''
instance-attribute
#
gltf = gltf_dict
instance-attribute
#
build_materials(renderer, pipeline)
#
Builds a real Material (via pipeline.create_material()) for
every entry in the glTF's materials array, keyed by material name
(a generated material_{i} for an unnamed one, or {name}_{i} if
that name collides with an earlier material). Relies on
self._textures (image_index -> loaded Texture) already being
populated - build_model() always builds textures before calling
this. Note glTF's own default baseColorFactor/roughnessFactor/
etc. are white/rough/metallic, not black - this mirrors those
defaults exactly rather than reusing whatever this engine's own
Material default happens to be.
build_model(model_name=None, scale=None)
#
Builds a Model from this glTF: every image is decoded and
uploaded once and every material is built once, both cached on
self (as _textures/_materials) so calling build_model()
again on the same parser - e.g. once per piece of a multi-mesh
file like a glTF chess set - doesn't redo that work.
model_name selects which mesh(es) to include: None (the default)
combines every mesh in the file into one Model, a string selects
the mesh with that exact name (raising ValueError if none
matches), and an int selects the mesh at that index directly -
needed because glTF mesh names aren't guaranteed unique. scale,
if given, is applied to every included mesh's raw vertex
positions.
Raises ValueError if the glTF has no meshes at all, or if
model_name doesn't resolve to any mesh.
get_accessor_data(accessor_index)
#
Returns a (count, num_components) NumPy array for the accessor - every caller already immediately wraps this in np.asarray()/ np.array(), so returning an array directly (instead of a Python list of tuples) is a transparent change, not an API break.
Reinterprets the raw buffer bytes straight into a strided NumPy view (np.ndarray(buffer=...)) rather than calling struct.unpack() once per element in a Python loop - for a glTF file with real vertex counts (tens of thousands to low millions for a detailed mesh), that per-element Python loop dominates load time; this does the equivalent reinterpretation as a single C-level pass.
get_image_data(image_index)
#
Returns the raw encoded bytes (PNG/JPEG, not yet decoded) of
image image_index - from the GLB BIN chunk, a base64 data: URI,
or (only when base_path was given) an external sibling file.
resolve_texture(texture_ref, textures)
#
Looks a glTF texture reference (e.g. a material's
baseColorTexture, {"index": ..., "texCoord": ...}) up in
textures (an image-index -> loaded-Texture map, as built by
build_model()) and returns the resolved Texture, or None if
texture_ref is None or names a texture/image that doesn't
exist.
bake_gltf_to_fbmesh(parser, image_name_prefix)
#
Pre-decodes every mesh primitive and material in parser's glTF into
a flat .fbmesh: a b"FBMH" magic, a little-endian uint32 giving the
length of a JSON header, that header, then a binary blob the header's
per-array {"offset", "length"} entries point into (each array 4-byte
aligned within the blob, so a reader can np.frombuffer() straight over
it with no copy or re-parse - see load_baked_model()).
This is intentionally independent of build_model()/build_materials()
above: those call get_service('renderer')/get_service('graphics')
to create real GL textures/materials immediately, which needs a live
GL context this build-time tool never has. Baked materials instead
reference textures by path - embedded images are extracted to real
files (returned as {relative_name: bytes} for the caller to bundle
alongside everything else) and loaded back through the normal
load_file()/load_texture() path at runtime, the same as any other
project image (including, in a release build, that image's own shared-
atlas packing).
Some material-building logic (default values, factor-vs-texture resolution) is deliberately mirrored from build_materials() rather than shared with it, since that version is wired directly to GL object creation - keep the two in sync by hand if glTF material handling ever changes.
load_baked_model(file)
#
Loads a .fbmesh written by bake_gltf_to_fbmesh() - every array is a
direct np.frombuffer() view over the file's own bytes (no JSON
accessor indirection, no per-element decode of any kind), and every
material's textures load through the normal load_file() path (so a
release build's shared texture atlas still applies to them).
load_model(file)
#
Loads a .gltf or .glb model file into a Model.
Checks build/builder.py's model manifest first - if this asset has a
pre-baked .fbmesh from a freebody build/dev build, loads that
instead (via load_baked_model(), no glTF parsing or texture decode at
all). Otherwise parses the glTF fresh on the spot (resolving any
external .bin/image siblings relative to the file's own directory)
and builds every mesh in it into one combined Model - callers that need
one specific mesh out of a multi-mesh file go through
GLTFParser.build_model() directly instead of this loader.
Raises ValueError if file's extension is neither .gltf nor .glb.