Optic
A modular OpenGL 4.6 engine for Rust. EGL native — same context API for windowed, headless, and compute. Used in production by one guy's hobby projects.
[]
= { = "https://github.com/Kono-o/optic-engine" }
Requires: OpenGL 4.6 GPU, EGL 1.5 drivers, Rust 1.70+.
Why optic
- No GLFW, no SDL, no glutin. Just EGL directly. One context, any number of windows, pbuffer surfaces for headless work. Zero window-manager coupling.
- Modular by default. Seven crates with clean dependency edges. Want only
the color library?
optic-coloris zero-dependency. Headless compute?optic-core + optic-render, no window crate needed. - No retained state guessing.
Eventsis a flat bit-field snapshot per frame. No callback spaghetti, no event queue to drain. Poll what you need. - Color types that actually work. HSV/HSL conversions that handle hue
wraparound. 115 named constants. A
Gradientbuilt on channel-arithmetic primitives so lerp doesn't give you wrong colors.
Quick start
use *;
;
Or with the simpler closure-based entry point:
use *;
Loading and rendering a 3D mesh
use *;
// GameBuilder::new().with_title("3D").build(App { .. }).unwrap().run();
Architecture
optic-color — standalone color library (~90 named constants, gradients)
optic-core — shared types, geometry, errors, ANSI logging
optic-file — read/write bytes/string, cached path resolution
optic-render — EGL context, GL wrappers, shaders, meshes, textures,
framebuffers (Canvas), storage buffers, instance buffers,
camera, asset loading (OBJ, PNG, GLSL parse)
optic-window — winit wrapper, per-frame key/mouse/gamepad state
optic-loop — Game + GameBuilder, Runtime trait, Time, frame pacing
optic-online — UDP networking on a background tokio thread (opt-in)
The optic meta-crate re-exports everything behind feature gates. Default
features give you the full stack. Cherry-pick what you need:
# headless compute only
= { = "..", = false, = ["core", "render"] }
# just colors
= { = ".." }
Feature highlights
Color
use *;
let bg = RGBAfrom_hex.unwrap;
let accent = GOLD_METALLIC;
let gradient = two_color
.set_color_space;
for i in 0..10
2D rendering with transforms
let mut mesh = game.renderer.add_mesh2d;
mesh.transform.set_pos;
mesh.transform.set_rot;
mesh.transform.set_scale;
mesh.update;
game.renderer.render2d;
Instanced rendering
let attrs = &;
let instances = game.renderer.add_instance_buffer;
mesh.set_instances;
// per-instance data
let data: = positions.iter.flat_map.collect;
instances.set_data;
Canvas (framebuffer)
let canvas = game.renderer.create_canvas;
game.renderer.render_to_canvas;
// canvas.color_texture(0) → use in a shader
Headless compute
use *;
let gpu = GPUnew_headless.unwrap;
let shader = gpu.create_shader;
shader.bind;
shader.dispatch;
// read back SSBO data with StorageBuffer
Asset caching
Optic bakes decoded assets into a binary cache with magic header validation
(/0PTIC_x). On subsequent loads it skips parsing entirely.
| Source | Cache |
|---|---|
.obj → parsed mesh |
.omesh |
.glsl → compiled (not cached) |
.oshdr (source post-process) |
.png / .jpg → decoded pixels |
.otxtr |
Use from_path_cached() on Mesh3DFile, ShaderFile, or TextureFile.
Networking (opt-in)
use *;
let config = host;
let game = new
.with_network
.build?
.enable_networking;
// In update(): game.network() → &NetworkHandle
// game.network().send_all(data);
API reference
Full documentation lives in docs/API.md.
License
MIT