Assets and import
The resource manager, typed asset handles and import settings.
Files: resource_manager.odin, assets.odin, asset_import.odin.
File overviews
Section titled “File overviews”assets.odin
Section titled “assets.odin”Typed asset handles: a project asset’s path, with its kind in the type, so code can’t play a texture or draw a sound. Scripts get them two ways:
- The editor writes
scripts/assets.odinwith a constant for every project asset (TEXTURE_SHIP,SOUND_LASER,DATA_GOBLIN, …): checked by the compiler, completed by the code editor, and only naming assets exports ship. - As exported fields (
ship: e.TextureAsset \export:“”``): the inspector shows a picker of the project’s assets of that kind.
Handles resolve against the project folder (Game.projectDir) and are loaded on first use, then
cached by the resource manager.
Example
package mygame
import e "engine:."
// What the editor writes in scripts/assets.odinSOUND_LASER :: e.SoundAsset{"assets/laser.wav"}TEXTURE_SHIP :: e.TextureAsset{"assets/ship.png"}DATA_FAST_SHIP :: e.DataAsset{"data/fast_ship.data.json"}
ShipStats :: struct { speed: f32 `export:"default=300"`,}
Fire :: proc(game: ^e.Game) { e.Game_PlaySound(game, SOUND_LASER) texture := e.Asset_Texture(game, TEXTURE_SHIP) stats := e.Data(ShipStats, DATA_FAST_SHIP) _, _ = texture, stats}asset_import.odin
Section titled “asset_import.odin”Import settings of a project’s assets: how its images are filtered, and which fonts have MSDF atlases (fonts.odin).
An image drawn bigger, smaller or turned is sampled either smoothly (linear filtering: soft
edges, right for painted art and photos) or by pixels (nearest: hard square pixels, right for
pixel art). Each image asset can say which (<asset path=... filter="Pixel"/>, AssetRef.filter),
else the project’s textureFilter decides, else Smooth for images and Pixel for Aseprite files.
Game_LoadProject hands the assets’ settings to the resource manager
(ResourceManager_SetImportSettings), which applies them to each texture it loads; editors call
ResourceManager_SetTextureFilter when one changes, which also changes the textures already
loaded.
ResourceManager
Section titled “ResourceManager”ResourceManager :: struct { // The renderer textures are made for renderer: ^sdl.Renderer, // The mixer sounds are loaded for mixer: ^mix.Mixer, // Loaded textures, by path (the keys are owned) textures: map[string]^sdl.Texture, // Each texture file's modification time when it was read (keys shared with `textures`), for // `ResourceManager_ReloadChangedTextures` textureTimes: map[string]i64, // Loaded sounds, by path (the keys are owned) audio: map[string]^mix.Audio, // Loaded fonts, by "path@size" (the keys are owned) fonts: map[string]^ttf.Font, // Images' filtering by path, as loadTexture is given it (asset_import.odin: the project's // assets; keys owned), and the filtering of the rest (Default: Smooth, Aseprite files Pixel) textureFilters: map[string]TextureFilter, defaultFilter: TextureFilter, // Bitmap fonts and MSDF fonts' atlases read so far, by path (nil: couldn't be read; fonts.odin) glyphFonts: map[string]^GlyphFont, // TrueType fonts that have an MSDF atlas (asset_import.odin; keys owned) msdfFonts: map[string]struct {}, // The shader MSDF fonts draw with (fonts.odin; made on first use) msdfProgram: ^ShaderProgram, // The embedded font at DEFAULT_FONT_SIZE, loaded by ResourceManager_Init defaultFont: ^ttf.Font, // Draws text through the renderer, caching glyphs in a GPU atlas. Used by the text primitives textEngine: ^ttf.TextEngine, // Loads an image (any format SDL_image reads: png, jpg, bmp, webp...) as a texture, or // returns the one already loaded. nil (logged) if it can't be read loadTexture: proc(rm: ^ResourceManager, filePath: string) -> ^sdl.Texture, // Loads a sound or music file, or returns the one already loaded. `predecode` decodes the // whole file up front (best for short effects); pass false to stream long music. nil (logged) // if it can't be read loadAudio: proc(rm: ^ResourceManager, filePath: string, predecode: bool = true) -> ^mix.Audio, // Loads a font at a point size (`DEFAULT_FONT` for the embedded one), or returns the one // already loaded at that size. nil (logged) if it can't be read loadFont: proc(rm: ^ResourceManager, filePath: string, size: f32) -> ^ttf.Font, // Frees a loaded texture. Anything still drawing it must stop first unloadTexture: proc(rm: ^ResourceManager, filePath: string), // Frees a loaded sound. Anything still playing it must stop first unloadAudio: proc(rm: ^ResourceManager, filePath: string), // Frees a font loaded at that size. Anything still using it must stop first unloadFont: proc(rm: ^ResourceManager, filePath: string, size: f32), // Frees the text engine and everything still loaded (`Game_Destroy` calls it) destroy: proc(rm: ^ResourceManager),}The game’s cache of textures, sounds and fonts (Game.resourceManager).
Assets are keyed by file path (fonts by path and size), so loading the same file twice returns
the handle already loaded. Paths are read as given (relative to the working directory, or
looked up in the game’s data pack). The manager owns what it loads: unload* frees one asset,
destroy (called by Game_Destroy) frees everything still loaded. Call the procedures with
->.
Example
rm := &game.resourceManagership := rm->loadTexture("assets/ship.png")laser := rm->loadAudio("assets/laser.wav")music := rm->loadAudio("assets/theme.ogg", predecode = false)title := rm->loadFont(e.DEFAULT_FONT, 48)_, _, _, _ = ship, laser, music, titlestruct. Source: resource_manager.odin:36
TextureAsset
Section titled “TextureAsset”TextureAsset :: struct { // The image's path, relative to the project folder ("" for none) path: string,}An image in the project (Sprite textures)
struct. Source: assets.odin:43
SoundAsset
Section titled “SoundAsset”SoundAsset :: struct { // The sound's path, relative to the project folder ("" for none) path: string,}A sound in the project
struct. Source: assets.odin:49
FontAsset
Section titled “FontAsset”FontAsset :: struct { // The font's path, relative to the project folder ("" for none) path: string,}A font in the project
struct. Source: assets.odin:55
DataAsset
Section titled “DataAsset”DataAsset :: struct { // The data file's path, relative to the project folder ("" for none) path: string,}A data resource in the project (data.odin): a .data.json file holding a registered data type
struct. Source: assets.odin:61
MusicAsset
Section titled “MusicAsset”MusicAsset :: struct { // The music file's path, relative to the project folder ("" for none) path: string,}Music in the project: streamed by a MusicPlayer as it plays (not loaded whole like a sound)
struct. Source: assets.odin:67
ComponentAsset
Section titled “ComponentAsset”ComponentAsset :: struct { // The component file's path, relative to the project folder ("" for none) path: string,}A component in the project (a saved node tree, .component.xml): Scene_AddComponent adds one
struct. Source: assets.odin:73
AnimationAsset
Section titled “AnimationAsset”AnimationAsset :: struct { // The file's path, relative to the project folder ("" for none) path: string,}An animation asset (an .anim.xml file, or an Aseprite file: its tags): animations AnimatedSprite
nodes share (AnimatedSprite_UseAnimations, animation_sets.odin)
struct. Source: assets.odin:80
LDtkAsset
Section titled “LDtkAsset”LDtkAsset :: struct { // The .ldtk file's path, relative to the project folder ("" for none) path: string,}An LDtk project in the project (.ldtk: ldtk.odin): its levels go in scenes as LDtkLevel nodes
struct. Source: assets.odin:86
TextureFilter
Section titled “TextureFilter”TextureFilter :: enum { // The project's setting (an asset's), or Smooth (Aseprite files: Pixel) for the project Default, // Linear filtering: soft, for painted art Smooth, // Nearest filtering: hard pixels, for pixel art Pixel,}How an image is sampled when drawn scaled or turned
enum. Source: asset_import.odin:21
Procedures
Section titled “Procedures”ResourceManager_Init
Section titled “ResourceManager_Init”ResourceManager_Init :: proc(rm: ^ResourceManager, renderer: ^sdl.Renderer, mixer: ^mix.Mixer)Sets up a resource manager: its maps and procedures, the text engine, and the default font
(DEFAULT_FONT at DEFAULT_FONT_SIZE). Game_Init calls it for Game.resourceManager.
Inputs
- rm: The resource manager
- renderer: The renderer textures and text are made for
- mixer: The mixer sounds are loaded for
procedure. Source: resource_manager.odin:93
ResourceManager_ReloadChangedTextures
Section titled “ResourceManager_ReloadChangedTextures”ResourceManager_ReloadChangedTextures :: proc(rm: ^ResourceManager) -> (reloaded: int)Reads again the textures whose files changed since they were loaded (saved in Aseprite or an image
editor), updating them in place: nodes keep drawing the same texture, now with the new pixels. A
file that changed size isn’t updated (logged): it shows when its scene is built again. Development
runs call it twice a second (Game_Update); editors call it for their previews.
Inputs
- rm: The resource manager
Returns
- How many textures were updated
procedure. Source: resource_manager.odin:169
Asset_LDtk
Section titled “Asset_LDtk”Asset_LDtk :: proc(game: ^Game, asset: LDtkAsset) -> ^LDtkProjectAn LDtk asset’s project, read the first time and kept for the game’s life (nil, logged, if it
can’t be read): Game_LoadLDtk.
Inputs
- game: The game that keeps it
- asset: The LDtk asset
Returns
- The project (the game’s: don’t free it), or nil for an empty asset or a file that can’t be read
Example
world := e.Asset_LDtk(game, LDTK_WORLD)for &level in world.worlds[0].levels do log.infof("%s: %v", level.identifier, level.size)procedure. Source: assets.odin:107
Asset_Texture
Section titled “Asset_Texture”Asset_Texture :: proc(game: ^Game, asset: TextureAsset) -> ^sdl.TextureA texture asset’s texture, loaded the first time and cached by the resource manager.
Inputs
- game: The game
- asset: The texture asset
Returns
- The texture (the resource manager’s: don’t destroy it), or nil for an empty asset or (logged) a file that can’t be loaded
Example
ship := e.Asset_Texture(game, TEXTURE_SHIP)e.DrawTexture(renderer, ship, {-32, -32, 64, 64})procedure. Source: assets.odin:128
Asset_Sound
Section titled “Asset_Sound”Asset_Sound :: proc(game: ^Game, asset: SoundAsset) -> ^mix.AudioA sound asset’s audio, loaded (decoded whole) the first time and cached by the resource manager.
Inputs
- game: The game
- asset: The sound asset
Returns
- The audio (the resource manager’s: don’t destroy it), or nil for an empty asset or (logged) a file that can’t be loaded
procedure. Source: assets.odin:144
Asset_Font
Section titled “Asset_Font”Asset_Font :: proc(game: ^Game, asset: FontAsset, size: f32) -> ^ttf.FontA font asset at a point size, loaded the first time and cached by the resource manager.
Inputs
- game: The game
- asset: The font asset
- size: The point size
Returns
- The font (the resource manager’s: don’t close it), or nil for an empty asset or (logged) a file that can’t be loaded
procedure. Source: assets.odin:161
Game_PlaySoundAsset
Section titled “Game_PlaySoundAsset”Game_PlaySoundAsset :: proc(game: ^Game, asset: SoundAsset) -> boolGame_PlaySound for a SoundAsset (see Game_PlaySoundFile); false (logged) for an empty one
procedure. Source: assets.odin:167
Game_PlaySound
Section titled “Game_PlaySound”Game_PlaySound :: proc{Game_PlaySoundAsset, Game_PlaySoundFile}Plays a sound once, loading it the first time (cached by the resource manager).
Inputs
- game: The game
- asset or path: A
SoundAsset, or a path relative to the project folder
Returns
- false if there’s no audio device, or (logged) the sound couldn’t be loaded or played
Example
e.Game_PlaySound(game, SOUND_LASER)e.Game_PlaySound(game, "assets/explosion.wav")procedure group. Source: assets.odin:190
ResourceManager_SetImportSettings
Section titled “ResourceManager_SetImportSettings”ResourceManager_SetImportSettings :: proc(rm: ^ResourceManager, assets: []AssetRef, projectDir: string, default: TextureFilter)Sets the resource manager’s import settings from a project: image filtering (the default and each image asset’s; textures already loaded change too) and which fonts have MSDF atlases.
Inputs
- rm: The resource manager
- assets: The project’s assets (
ProjectData.assets) - projectDir: Their folder (what their paths are joined to when loaded)
- default: The project’s filtering (
ProjectData.textureFilter)
procedure. Source: asset_import.odin:40
ResourceManager_SetTextureFilter
Section titled “ResourceManager_SetTextureFilter”ResourceManager_SetTextureFilter :: proc(rm: ^ResourceManager, path: string, filter: TextureFilter)Sets one image’s filtering (Default: the project’s), the texture too if it’s loaded (its Aseprite sheets and layers with it).
Inputs
- rm: The resource manager
- path: The image’s path as textures are loaded with it (the project folder joined)
- filter: Its filtering
procedure. Source: asset_import.odin:69
ResourceManager_TextureFilter
Section titled “ResourceManager_TextureFilter”ResourceManager_TextureFilter :: proc(rm: ^ResourceManager, path: string) -> TextureFilterThe filtering an image is drawn with: its own, the project’s, else Smooth (Aseprite files Pixel).
Inputs
- rm: The resource manager
- path: The image’s file
procedure. Source: asset_import.odin:90
Constants and variables
Section titled “Constants and variables”DEFAULT_FONT
Section titled “DEFAULT_FONT”DEFAULT_FONT :: "engine:default_font"Open Sans Regular, compiled into the binary. Pass DEFAULT_FONT as the path to loadFont
to get it at other sizes
constant. Source: resource_manager.odin:13
DEFAULT_FONT_SIZE
Section titled “DEFAULT_FONT_SIZE”DEFAULT_FONT_SIZE :: 16The point size of ResourceManager.defaultFont
constant. Source: resource_manager.odin:15
