Skip to content

Commit a7a55f3

Browse files
authored
Merge pull request #4 from schwwaaa/ndi
NDI integrated (macOS tested/validated)
2 parents 903c990 + 0c78b92 commit a7a55f3

File tree

13 files changed

+1216
-167
lines changed

13 files changed

+1216
-167
lines changed

Cargo.lock

Lines changed: 272 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@ name = "shadecore"
33
version = "0.1.0"
44
edition = "2021"
55

6-
# Segment 4 (macOS): Syphon output
7-
# - Render shader into an FBO texture
8-
# - Publish that texture via Syphon (Objective-C bridge)
9-
# - Still display it in the window
10-
116
[dependencies]
127
glow = "0.16"
138
winit = "0.29"
@@ -21,6 +16,14 @@ serde_json = "1"
2116
midir = "0.10"
2217
anyhow = "1"
2318

19+
# Optional NDI output (requires NDI SDK installed; build with --features ndi)
20+
# Using grafton-ndi 0.9.x API (PixelFormat/ScanType/VideoFrame builder, etc.)
21+
grafton-ndi = { version = "0.9", optional = true }
22+
23+
[features]
24+
default = []
25+
ndi = ["dep:grafton-ndi"]
26+
2427
[build-dependencies]
2528
cc = "1"
2629
cmake = "0.1"
@@ -30,4 +33,4 @@ anyhow = "1"
3033
libloading = "0.8"
3134

3235
[lints.rust]
33-
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_syphon)'] }
36+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_syphon)'] }

README.md

Lines changed: 166 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,102 +2,180 @@
22
<img width="45%" height="45%" src="https://github.com/schwwaaa/shadecore/blob/main/media/shadecore-logo.png?raw=true"/>
33
</p>
44

5-
<p align="center"><em>A native, highperformance GLSL rendering engine written in Rust, designed for realtime shader experimentation, MIDI control, and live video routing.</em></p>
5+
<p align="center"><em>A native, high-performance GLSL rendering engine written in Rust, designed for real-time shader experimentation, hardware control, and live video routing.</em></p>
66

77
---
88

99
## Description
1010

11-
`shadecore` is a **standalone OpenGL shader engine** that renders a fullscreen GLSL fragment shader and can route the output as:
11+
`shadecore` is a **standalone OpenGL shader engine** that renders a fullscreen GLSL fragment shader and routes the result to multiple real-time video outputs.
1212

13-
- local window preview (FBO texture)
13+
Supported outputs:
14+
15+
- Local window preview (always on)
1416
- **Syphon** (macOS)
1517
- **Spout2** (Windows)
16-
- **Stream** via FFmpeg (RTSP for local network; RTMP for platforms)
18+
- **FFmpeg streaming** (RTSP / RTMP)
19+
- **NDI** (separate execution mode)
20+
21+
The engine is intentionally **low-level and explicit**:
1722

18-
It is designed to be:
19-
- fast enough for feedback systems,
20-
- deterministic enough for installations,
21-
- flexible enough to act as a base for many future tools.
23+
- No GUI framework
24+
- No WebView
25+
- No runtime abstraction layer between your shader and the GPU
2226

23-
There is **no GUI framework**, **no WebView**, and **no runtime abstraction layer** between your shader and the GPU.
27+
What you write in GLSL is what runs.
2428

2529
---
2630

2731
## Purpose
2832

29-
This project exists to solve a common problem in creative coding:
33+
This project exists to solve a common creative-coding problem:
3034

3135
> *“I want to build my own visual tools without shipping an entire framework.”*
3236
33-
`shadecore` is intended to be:
34-
- a **foundation** for custom shader‑based applications,
37+
`shadecore` is designed to be:
38+
39+
- a **foundation** for custom shader-based tools,
3540
- a **bridge** between GLSL and external control systems,
36-
- a **standalone binary** rather than a patch inside another tool.
41+
- a **standalone binary**, not a plugin locked into another host.
42+
43+
It is equally suited for:
44+
- live performance,
45+
- installations,
46+
- research tools,
47+
- experimental pipelines.
3748

3849
---
3950

4051
## Features
4152

4253
- Native OpenGL rendering (via `glow`)
4354
- Fullscreen GLSL fragment shader pipeline
44-
- MIDI parameter control (CoreMIDI)
45-
- JSON‑defined parameter schema
46-
- Syphon server output (macOS)
47-
- Spout2 sender output (Windows)
48-
- FFmpeg streaming output (RTSP/RTMP)
49-
- Vendored framework dependencies (no system installs for Syphon/Spout)
55+
- JSON-defined parameter schema
56+
- MIDI control (CoreMIDI on macOS, cross-platform via `midir`)
57+
- **Syphon server output** (macOS)
58+
- **Spout2 sender output** (Windows)
59+
- **FFmpeg streaming output** (RTSP / RTMP)
60+
- **NDI output (separate run mode)**
61+
- Vendored native dependencies (no system installs required)
5062
- Deterministic build & runtime behavior
5163

5264
---
5365

5466
## Running the Project
5567

5668
### Requirements
57-
- macOS or Windows (Linux builds for local preview are possible)
58-
- Rust (stable)
5969

60-
Platform extras:
61-
- macOS: Xcode Command Line Tools (Syphon.framework is vendored)
62-
- Windows: Visual Studio Build Tools (Spout2 is vendored)
63-
- Streaming: FFmpeg available in PATH, or set `stream.ffmpeg_path` in `assets/output*.json`
70+
- macOS or Windows
71+
- Rust (stable toolchain)
72+
73+
Platform-specific:
74+
75+
- **macOS**
76+
- Xcode Command Line Tools
77+
- Syphon.framework is vendored (no install required)
78+
- **Windows**
79+
- Visual Studio Build Tools (C++ workload)
80+
- Spout2 is vendored
81+
- **Streaming**
82+
- FFmpeg available in `PATH`
83+
- or set `stream.ffmpeg_path` in `assets/output*.json`
6484

65-
### Build & Run
85+
---
86+
87+
## Build & Run (Standard Engine)
6688

6789
```bash
6890
cargo run
6991
```
7092

7193
This will:
94+
7295
- compile the engine
73-
- launch the renderer
74-
- load defaults from `assets/params.json` and `assets/output.json`
75-
- show a local preview window (always)
76-
77-
Switch outputs at runtime (defaults, configurable in `assets/output*.json`):
78-
- `1` = Texture (preview only)
79-
- `2` = Syphon (macOS)
80-
- `3` = Spout2 (Windows)
81-
- `4` = Stream (FFmpeg RTSP/RTMP)
96+
- launch the OpenGL renderer
97+
- load:
98+
- `assets/params.json`
99+
- `assets/output.json`
100+
- open a local preview window (**always active**)
101+
82102
---
83103

84-
## Project Structure
104+
## Output Routing
105+
106+
Output behavior is controlled by `assets/output.json` (or alternate output configs).
107+
108+
### Runtime Hotkeys (default)
109+
110+
- `1` — Texture only (preview)
111+
- `2` — Syphon (macOS)
112+
- `3` — Spout2 (Windows)
113+
- `4` — Stream (FFmpeg RTSP / RTMP)
114+
- `6` — NDI (see below)
115+
116+
Hotkeys are configurable in the output JSON.
117+
118+
---
119+
120+
## ⚠️ NDI Output (Important)
121+
122+
NDI is **not enabled in the default execution path**.
123+
124+
This is intentional.
125+
126+
### Why NDI Is Separate
127+
128+
NDI requires:
129+
- a different runtime lifecycle,
130+
- different threading assumptions,
131+
- tighter timing guarantees.
132+
133+
Rather than complicate the core render loop, NDI runs in a **dedicated execution mode**.
134+
135+
### Running with NDI
85136

137+
```bash
138+
cargo run --features ndi
139+
```
140+
141+
or
142+
143+
```bash
144+
cargo run --bin shadecore-ndi
86145
```
146+
147+
Check `Cargo.toml` for the active NDI configuration.
148+
149+
### NDI Notes
150+
151+
- NDI output is discoverable by OBS, Resolume, and other NDI-capable software
152+
- Local preview still runs unless explicitly disabled
153+
- NDI uses its own output configuration file
154+
155+
This separation is **by design**, not a limitation.
156+
157+
---
158+
159+
## Project Structure
160+
161+
```text
87162
shadecore/
88163
├─ src/
89164
│ └─ main.rs # Core engine loop
90165
├─ native/
91-
│ ├─ spout_bridge/ # C++ Spout2 bridge (Windows)
92-
│ ├─ syphon_bridge.m # ObjectiveC Syphon bridge
166+
│ ├─ spout_bridge/ # C++ Spout2 bridge (Windows)
167+
│ ├─ syphon_bridge.m # Objective-C Syphon bridge (macOS)
93168
│ └─ syphon_bridge.h
94169
├─ vendor/
95-
│ └─ Syphon.framework # Vendored framework
170+
│ └─ Syphon.framework # Vendored macOS framework
96171
├─ assets/
97-
│ ├─ params.json # Parameter & MIDI schema
98-
│ ├─ output.json # Output routing (mode, hotkeys, stream settings)
99-
│ └─ shaders/ # default.frag / present.frag
100-
├─ build.rs # Framework linking + rpath logic
172+
│ ├─ params.json # Parameters + MIDI schema
173+
│ ├─ output.json # Output routing & hotkeys
174+
│ ├─ output_ndi.json # NDI-specific configuration
175+
│ └─ shaders/
176+
│ ├─ default.frag
177+
│ └─ present.frag
178+
├─ build.rs # Native linking & platform logic
101179
└─ Cargo.toml
102180
```
103181

@@ -106,47 +184,53 @@ shadecore/
106184
## Dependencies
107185

108186
### Rust Crates
109-
- `glow` – OpenGL bindings
110-
- `winit` / `glutin` – window + context
111-
- `midir` – MIDI input
112-
- `serde` / `serde_json` – parameter parsing
113187

114-
### Native Frameworks
188+
- `glow` — OpenGL bindings
189+
- `winit` / `glutin` — windowing + GL context
190+
- `midir` — MIDI input
191+
- `serde` / `serde_json` — configuration parsing
192+
193+
### Native APIs
194+
115195
- OpenGL
116-
- Cocoa / AppKit
196+
- Cocoa / AppKit (macOS)
117197
- CoreMIDI
118-
- **Syphon** (vendored)
198+
- Syphon (vendored)
199+
- Spout2 (vendored)
119200

120201
---
121202

122203
## How It Works
123204

124205
### 1. OpenGL Rendering
125-
- A window and OpenGL context are created using `winit` + `glutin`
126-
- A fullscreen triangle is drawn every frame
127-
- The fragment shader is responsible for all visual output
206+
207+
- A window and GL context are created with `winit` + `glutin`
208+
- A fullscreen triangle is rendered every frame
209+
- All visuals are produced in the fragment shader
128210

129211
### 2. Shader Uniforms
130-
The engine provides:
131-
- `u_time` – seconds since start
132-
- `u_resolution` – window size in pixels
133-
- user‑defined parameters (from JSON + MIDI)
212+
213+
Built-in uniforms:
214+
215+
- `u_time` — seconds since start
216+
- `u_resolution` — framebuffer resolution
217+
218+
Plus:
219+
- user-defined parameters from JSON
220+
- live-updated MIDI values
134221

135222
### 3. Render Target
223+
136224
- Rendering occurs into an offscreen framebuffer
137225
- The framebuffer texture is:
138-
- drawn to the window
139-
- published to Syphon
140-
141-
### 4. Syphon Publishing
142-
- The OpenGL texture ID is passed to Syphon
143-
- Other apps can receive frames in real time
226+
- drawn to the preview window
227+
- shared with Syphon, Spout, Stream, or NDI depending on mode
144228

145229
---
146230

147231
## Parameters & MIDI
148232

149-
Parameters are defined in a JSON file:
233+
Parameters are defined declaratively in JSON.
150234

151235
```json
152236
{
@@ -164,40 +248,45 @@ Parameters are defined in a JSON file:
164248
}
165249
```
166250

251+
Behavior:
252+
167253
- MIDI CC values (0–127) are normalized
168-
- Values are scaled into parameter ranges
169-
- Parameters are updated every frame
254+
- Values are mapped into parameter ranges
255+
- Parameters update every frame
256+
- No hidden smoothing or automation
170257

171-
This makes controller layouts reproducible and portable.
258+
Controller layouts are portable and reproducible.
172259

173260
---
174261

175262
## Use Cases
176263

177264
- Live shader performance
178265
- Visual instruments
179-
- Generative art installations
180-
- Feedbackbased video systems
181-
- Custom tools for OBS / Resolume / TouchDesigner pipelines
266+
- Generative installations
267+
- Feedback-based video systems
268+
- Custom GPU tools for OBS, Resolume, TouchDesigner pipelines
182269

183270
---
184271

185272
## Roadmap
186273

187-
- Hot‑loading shaders from disk
188-
- Multi‑pass rendering / feedback buffers
189-
- `.app` bundling
190-
- Windows backend (Spout)
274+
- Shader hot-reloading
275+
- Multi-pass rendering / feedback buffers
276+
- `.app` / `.exe` packaging
191277
- OSC / network control
278+
- Expanded NDI configuration options
192279

193280
---
194281

195282
## Philosophy
196283

197-
This tool is intentionally **minimal and explicit**.
284+
`shadecore` is intentionally **minimal, explicit, and opinionated**.
285+
286+
It does not try to be a framework.
287+
It does not hide the GPU.
198288

199-
Instead of abstracting creativity behind interfaces,
200-
`shadecore` gives you **direct control of the GPU** and lets you decide what the software should become.
289+
It exists to give you **direct ownership of the rendering pipeline** and let the software grow into whatever tool you need.
201290

202291
---
203292

0 commit comments

Comments
 (0)