Skip to content

Resources

Resources in Pill are shared assets like meshes, textures, materials, and audio clips. They are loaded once and can be referenced by multiple entities.

Resource Management

Resources are managed through handles - lightweight references to the actual resource data. This allows multiple entities to share the same asset without duplication.

Resource Types

  • Mesh: 3D geometry data
  • Texture: Image data for materials
  • Material: Defines surface appearance
  • AudioClip: Sound data for audio playback

Meshes

Meshes contain the 3D geometry of objects.

Loading Meshes

Load mesh files from your res/models/ directory:

rust
let mesh_handle = engine.add_resource(
    Mesh::new("Truck", "models/Truck.obj".into())
)?;

Supported Formats

  • OBJ (Wavefront): Standard 3D format
    • Supports vertices, normals, and UV coordinates
    • Can include multiple objects in one file

Using Meshes

Attach meshes to entities with MeshRenderingComponent:

rust
engine.build_entity(scene)
    .with_component(TransformComponent::default())
    .with_component(MeshRenderingComponent::builder()
        .mesh(&mesh_handle)
        .material(&material_handle)
        .build())
    .build();

Best Practices

  • Optimize geometry: Keep polygon count reasonable for real-time rendering
  • Share meshes: Reuse mesh handles for multiple entities
  • Proper normals: Ensure your models have correct normals for lighting
rust
// Good: One mesh, many entities
let crate_mesh = engine.add_resource(Mesh::new("Crate", "models/Crate.obj".into()))?;

for i in 0..10 {
    engine.build_entity(scene)
        .with_component(TransformComponent::builder()
            .position(Vector3f::new(i as f32 * 2.0, 0.0, 0.0))
            .build())
        .with_component(MeshRenderingComponent::builder()
            .mesh(&crate_mesh) // Shared mesh handle
            .material(&material)
            .build())
        .build();
}

Textures

Textures are images applied to 3D surfaces.

Loading Textures

Load texture files from your res/textures/ directory:

rust
let color_texture = engine.add_resource::<Texture>(
    Texture::new(
        "WoodColor",
        TextureType::Color,
        ResourceLoadType::Path("textures/Wood.png".into())
    )
)?;

Texture Types

Different texture types serve different purposes:

Color (Albedo)

Base color of the surface:

rust
Texture::new(
    "BaseColor",
    TextureType::Color,
    ResourceLoadType::Path("textures/Color.png".into())
)

Normal Maps

Add surface detail without geometry:

rust
Texture::new(
    "NormalMap",
    TextureType::Normal,
    ResourceLoadType::Path("textures/Normal.png".into())
)

Roughness/Metallic

PBR material properties:

rust
Texture::new(
    "Roughness",
    TextureType::Roughness,
    ResourceLoadType::Path("textures/Roughness.png".into())
)

Supported Formats

  • PNG (recommended for transparency)
  • JPEG (good for photos)
  • BMP, TGA, HDR

Texture Guidelines

  1. Power of 2 dimensions: Use 256x256, 512x512, 1024x1024, etc.
  2. Compression: Keep file sizes reasonable
  3. Mipmaps: The engine generates these automatically
  4. UV mapping: Ensure your 3D models have proper UV coordinates

Materials

Materials define how surfaces appear by combining textures and parameters.

Creating Materials

Use the material builder:

rust
let material = engine.add_resource::<Material>(
    Material::builder("WoodMaterial")
        .texture("Color", color_texture_handle)?
        .texture("Normal", normal_texture_handle)?
        .color("Tint", Color::new(1.0, 1.0, 1.0))?
        .scalar("Specularity", 0.5)?
        .build()
)?;

Material Properties

Textures

Assign textures to material slots:

rust
Material::builder("MyMaterial")
    .texture("Color", color_texture)?      // Base color
    .texture("Normal", normal_texture)?    // Normal map
    .texture("Roughness", rough_texture)?  // Roughness map

Colors

Tint and modify appearance:

rust
Material::builder("MyMaterial")
    .color("Tint", Color::new(1.0, 0.5, 0.2))?  // Orange tint
    .color("Emissive", Color::new(1.0, 1.0, 1.0))?  // Glow color

Scalars

Numeric parameters:

rust
Material::builder("MyMaterial")
    .scalar("Specularity", 0.8)?   // 0.0 = matte, 1.0 = glossy
    .scalar("Metallic", 0.0)?      // 0.0 = dielectric, 1.0 = metal
    .scalar("Roughness", 0.3)?     // Surface roughness
    .scalar("Opacity", 1.0)?       // Transparency

Complete Material Example

rust
// Load textures
let wood_color = engine.add_resource::<Texture>(
    Texture::new(
        "WoodColor",
        TextureType::Color,
        ResourceLoadType::Path("textures/WoodColor.png".into())
    )
)?;

let wood_normal = engine.add_resource::<Texture>(
    Texture::new(
        "WoodNormal",
        TextureType::Normal,
        ResourceLoadType::Path("textures/WoodNormal.png".into())
    )
)?;

// Create material
let wood_material = engine.add_resource::<Material>(
    Material::builder("Wood")
        .texture("Color", wood_color)?
        .texture("Normal", wood_normal)?
        .color("Tint", Color::new(0.8, 0.6, 0.4))?  // Brown tint
        .scalar("Specularity", 0.3)?                 // Slightly glossy
        .build()
)?;

// Use in entity
engine.build_entity(scene)
    .with_component(TransformComponent::default())
    .with_component(MeshRenderingComponent::builder()
        .mesh(&crate_mesh)
        .material(&wood_material)
        .build())
    .build();

Modifying Materials at Runtime

Get mutable access to modify materials during gameplay:

rust
let material = engine.get_resource_mut::<Material>(&material_handle)?;
material.set_color("Tint", Color::new(1.0, 0.0, 0.0))?;  // Change to red
material.set_scalar("Specularity", 0.9)?;  // Make more glossy

This is useful for:

  • Damage effects (changing color/texture)
  • Power-ups (glowing effects)
  • Environmental changes (wet/dry surfaces)

Audio Resources

Audio clips for sound effects and music.

Loading Audio

rust
let audio_clip = engine.add_resource::<AudioClip>(
    AudioClip::new("Explosion", "audio/explosion.wav".into())
)?;

Using Audio

Attach audio sources to entities:

rust
engine.build_entity(scene)
    .with_component(TransformComponent::builder()
        .position(Vector3f::new(10.0, 0.0, 0.0))
        .build())
    .with_component(AudioSourceComponent::builder()
        .audio_clip(&audio_clip)
        .volume(0.8)
        .looping(false)
        .spatial(true)  // 3D positional audio
        .build())
    .build();

Audio Listener

Add an audio listener (typically to the camera):

rust
engine.build_entity(scene)
    .with_component(TransformComponent::default())
    .with_component(CameraComponent::default())
    .with_component(AudioListenerComponent::builder()
        .enabled(true)
        .build())
    .build();

Supported Formats

  • WAV (uncompressed, best quality)
  • OGG (compressed, good for music)
  • MP3 (compressed, widely supported)

Resource Organization

Directory Structure

Organize your resources in the res/ directory:

res/
├── config.ini
├── models/
│   ├── Player.obj
│   ├── Enemy.obj
│   ├── Weapon.obj
│   └── Environment.obj
├── textures/
│   ├── PlayerColor.png
│   ├── PlayerNormal.png
│   ├── EnemyColor.png
│   └── GroundDirt.png
└── audio/
    ├── footstep.wav
    ├── gunshot.wav
    └── music.ogg

Naming Conventions

Use clear, descriptive names:

rust
// Good naming
let player_mesh = engine.add_resource(Mesh::new("PlayerMesh", "models/Player.obj".into()))?;
let player_color = engine.add_resource::<Texture>(
    Texture::new("PlayerColorTexture", TextureType::Color, ResourceLoadType::Path("textures/PlayerColor.png".into()))
)?;

// Avoid generic names
let mesh1 = engine.add_resource(Mesh::new("Mesh1", "models/Player.obj".into()))?;
let tex = engine.add_resource::<Texture>(
    Texture::new("Tex", TextureType::Color, ResourceLoadType::Path("textures/PlayerColor.png".into()))
)?;

Resource Loading Patterns

Preload Resources

Load all resources during start():

rust
impl PillGame for Game {
    fn start(&self, engine: &mut Engine) -> Result<()> {
        // Load all resources upfront
        let meshes = load_all_meshes(engine)?;
        let materials = load_all_materials(engine)?;
        
        // Then create entities using loaded resources
        create_entities(engine, &meshes, &materials)?;
        
        Ok(())
    }
}

fn load_all_meshes(engine: &mut Engine) -> Result<HashMap<String, MeshHandle>> {
    let mut meshes = HashMap::new();
    
    meshes.insert("Player".into(), 
        engine.add_resource(Mesh::new("Player", "models/Player.obj".into()))?);
    meshes.insert("Enemy".into(),
        engine.add_resource(Mesh::new("Enemy", "models/Enemy.obj".into()))?);
    
    Ok(meshes)
}

Store Handles

Keep resource handles for easy access:

rust
pub struct GameResources {
    // Meshes
    pub player_mesh: MeshHandle,
    pub enemy_mesh: MeshHandle,
    
    // Materials
    pub player_material: MaterialHandle,
    pub enemy_material: MaterialHandle,
    
    // Audio
    pub shoot_sound: AudioClipHandle,
}

impl GlobalComponent for GameResources {}
impl PillTypeMapKey for GameResources {
    type Storage = GlobalComponentStorage<Self>;
}

// Load and store
let resources = GameResources {
    player_mesh: engine.add_resource(Mesh::new("Player", "models/Player.obj".into()))?,
    enemy_mesh: engine.add_resource(Mesh::new("Enemy", "models/Enemy.obj".into()))?,
    // ... etc
};
engine.add_global_component(resources)?;

// Use in systems
fn spawn_enemy_system(engine: &mut Engine) -> Result<()> {
    let resources = engine.get_global_component::<GameResources>()?;
    
    engine.build_entity(scene)
        .with_component(TransformComponent::default())
        .with_component(MeshRenderingComponent::builder()
            .mesh(&resources.enemy_mesh)
            .material(&resources.enemy_material)
            .build())
        .build();
    
    Ok(())
}

Advanced: Procedural Materials

Create materials programmatically for dynamic effects:

rust
fn create_team_materials(engine: &mut Engine, base_texture: TextureHandle) -> Result<Vec<MaterialHandle>> {
    let colors = vec![
        Color::new(1.0, 0.0, 0.0),  // Red team
        Color::new(0.0, 0.0, 1.0),  // Blue team
        Color::new(0.0, 1.0, 0.0),  // Green team
    ];
    
    let mut materials = Vec::new();
    for (i, color) in colors.iter().enumerate() {
        let material = engine.add_resource::<Material>(
            Material::builder(&format!("Team{}", i))
                .texture("Color", base_texture)?
                .color("Tint", *color)?
                .scalar("Specularity", 0.5)?
                .build()
        )?;
        materials.push(material);
    }
    
    Ok(materials)
}

Performance Considerations

Resource Sharing

Always share resources when possible:

rust
// Good: One mesh for 100 entities
let mesh = engine.add_resource(Mesh::new("Cube", "models/Cube.obj".into()))?;
let material = engine.add_resource::<Material>(Material::builder("Blue").build())?;

for i in 0..100 {
    engine.build_entity(scene)
        .with_component(TransformComponent::builder()
            .position(Vector3f::new(i as f32, 0.0, 0.0))
            .build())
        .with_component(MeshRenderingComponent::builder()
            .mesh(&mesh)      // Shared!
            .material(&material)  // Shared!
            .build())
        .build();
}

Texture Resolution

Balance quality and performance:

  • UI elements: 512x512 or smaller
  • Character textures: 1024x1024 to 2048x2048
  • Environment: 2048x2048 for close-up, 512x512 for distant
  • Skybox: 2048x2048 or 4096x4096

Loading Time

Consider async loading for large resources:

rust
// For now, load everything in start()
// Future: Async resource loading system

Next Steps

Now that you understand resources, continue to: