feat: integrate TUI module and apply professional refactoring
This commit is contained in:
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to the ASCII 3D Renderer project will be documented in this file.
|
||||||
|
|
||||||
|
## [3.0.0] - 2026-04-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Interactive TUI**: A fully non-blocking TTY mode using `termios.h`, eliminating the requirement to restart the command line tool to view varying text or configuration.
|
||||||
|
- **Dynamic Text Buffering**: Real-time alphanumeric typing support overlaying the 3D rotating model.
|
||||||
|
- **Keybinding Overlay**: Rendered a helpful overlay bar detailing available interaction toggles at the bottom of the viewport matrix (`space` to pause/play, `c` to toggle color mappings, `w/s` to manage rotation speed).
|
||||||
|
- **Core TUI Module**: Explicit `tui.h` and `tui.c` segregation strictly following clean engineering isolation paradigms.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Removed Boilerplate Commentary**: Stripped all heavy `Doxygen` noise that obscured the raw logic of the C11 source codes. Instead, injected strict, professional, direct inline comments focusing on low-level 'why' rather than regurgitating standard boilerplate.
|
||||||
|
- **Makefile Restructure**: Abstracted the build targets for greater directness and improved comment clarity.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Stabilized terminal behavior: enforced cleanup handlers to restore standard canonical reading and buffer modes gracefully upon process interrupt or `quit` signaling, minimizing occurrences of a broken terminal interface.
|
||||||
32
DOCUMENTATION.md
Normal file
32
DOCUMENTATION.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# ASCII 3D Renderer - Engineering Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
ASCII 3D Renderer is a sophisticated terminal-based rendering engine implemented entirely in C11. Its goal is to bring modern 3D rendering methodologies—such as Physical-Based Lighting approximations and Depth Buffering—into a constrained ASCII viewport without utilizing external graphical libraries.
|
||||||
|
|
||||||
|
## Technical Architecture
|
||||||
|
|
||||||
|
The codebase adheres strictly to functional abstractions and strict modular boundaries to enhance performance within deep rendering loops.
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
- `include/`: Enforces API boundaries and struct definitions across all C modules.
|
||||||
|
- `src/`: Houses the isolated functional algorithms driving vectors, physics, and input multiplexing.
|
||||||
|
|
||||||
|
### Subsystem Design
|
||||||
|
|
||||||
|
1. **Input multiplexing (`tui.c`)**
|
||||||
|
Toggling standard IO into non-blocking, non-canonical states permits smooth real-time TTY interactions. Signal states are captured instantaneously and interpreted within the main event-loop per tick, altering global behavior objects without disrupting the core voxel algorithms.
|
||||||
|
|
||||||
|
2. **Core 3D Engine (`renderer.c`)**
|
||||||
|
Calculates pseudo-normals via 3D vector traversal. Text characters initially manifest as two-dimensional `5x7` bitmaps encoded as raw bitflags (`font.c`). `renderer.c` mathematically extrudes these glyphs along a virtual Z-axis. Multi-sampled pixel rays compute intersection boundaries and calculate normal approximations by reading empty adjacencies along geometry ridges.
|
||||||
|
|
||||||
|
3. **Lighting Approximations (`lighting.c`)**
|
||||||
|
Standard Blinn-Phong equations exist within an ambient, diffuse, and specular composition loop. These color values are derived utilizing an arbitrary virtual camera and bounded to integer offsets matching one of our standard luminance dictionaries (e.g., `.:-=+*#%@`).
|
||||||
|
|
||||||
|
4. **Temporal Control (`timing.c`)**
|
||||||
|
Forces a consistent execution boundary using positive UNIX epoch resolution (`CLOCK_MONOTONIC`) mapped directly via `nanosleep`. Predictable delta steps stabilize the rotation matrix speed regardless of hardware IO constraints.
|
||||||
|
|
||||||
|
## Compilation and Dependencies
|
||||||
|
|
||||||
|
The system is pure logic—`libc` and `-lm` (for `libmath`) constitute the full scope of integration dependencies. The custom `Makefile` orchestrates compilation utilizing Standard `-O3 -flto` link time passes, alongside memory sanitizer profiles natively integrated within Clang for debugging cycles.
|
||||||
69
Makefile
69
Makefile
@@ -3,123 +3,80 @@ INCDIR := include
|
|||||||
OBJDIR := obj
|
OBJDIR := obj
|
||||||
BINDIR := bin
|
BINDIR := bin
|
||||||
|
|
||||||
# Compiler and flags
|
# Core compiler config: we favor strict standards and high warnings for C11
|
||||||
CC := clang
|
CC := clang
|
||||||
CFLAGS := -std=c11 -Wall -Wextra -Wpedantic -Werror
|
CFLAGS := -std=c11 -Wall -Wextra -Wpedantic -Werror \
|
||||||
CFLAGS += -Wshadow -Wconversion -Wdouble-promotion
|
-Wshadow -Wconversion -Wdouble-promotion \
|
||||||
CFLAGS += -Wformat=2 -Wundef -fno-common
|
-Wformat=2 -Wundef -fno-common -I$(INCDIR)
|
||||||
CFLAGS += -I$(INCDIR)
|
|
||||||
|
|
||||||
# Target
|
|
||||||
TARGET := $(BINDIR)/ascii3d
|
TARGET := $(BINDIR)/ascii3d
|
||||||
|
|
||||||
# Source files
|
|
||||||
SRCS := $(wildcard $(SRCDIR)/*.c)
|
SRCS := $(wildcard $(SRCDIR)/*.c)
|
||||||
OBJS := $(SRCS:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
|
OBJS := $(SRCS:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
|
||||||
DEPS := $(OBJS:.o=.d)
|
DEPS := $(OBJS:.o=.d)
|
||||||
|
|
||||||
# Libraries
|
# Linking libm for math logic
|
||||||
LDFLAGS := -lm
|
LDFLAGS := -lm
|
||||||
|
|
||||||
# Build modes
|
.PHONY: all debug release clean install uninstall help tui
|
||||||
.PHONY: all debug release clean install uninstall help
|
|
||||||
|
|
||||||
# Default target
|
|
||||||
all: release
|
all: release
|
||||||
|
|
||||||
# Release build (optimized)
|
# Standard LTO and extreme optimization flags for our main build
|
||||||
release: CFLAGS += -O3 -DNDEBUG -march=native -flto
|
release: CFLAGS += -O3 -DNDEBUG -march=native -flto
|
||||||
release: LDFLAGS += -flto
|
release: LDFLAGS += -flto
|
||||||
release: $(TARGET)
|
release: $(TARGET)
|
||||||
|
|
||||||
# Debug build (with symbols and sanitizers)
|
# Build configuration suitable for gdb integration and memory sanity checking
|
||||||
debug: CFLAGS += -O0 -g3 -DDEBUG
|
debug: CFLAGS += -O0 -g3 -DDEBUG -fsanitize=address,undefined
|
||||||
debug: CFLAGS += -fsanitize=address,undefined
|
|
||||||
debug: LDFLAGS += -fsanitize=address,undefined
|
debug: LDFLAGS += -fsanitize=address,undefined
|
||||||
debug: $(TARGET)
|
debug: $(TARGET)
|
||||||
|
|
||||||
# Profile build (optimized with debug symbols)
|
|
||||||
profile: CFLAGS += -O2 -g -pg
|
profile: CFLAGS += -O2 -g -pg
|
||||||
profile: LDFLAGS += -pg
|
profile: LDFLAGS += -pg
|
||||||
profile: $(TARGET)
|
profile: $(TARGET)
|
||||||
|
|
||||||
# Create directories
|
|
||||||
$(OBJDIR) $(BINDIR):
|
$(OBJDIR) $(BINDIR):
|
||||||
@mkdir -p $@
|
@mkdir -p $@
|
||||||
|
|
||||||
# Link
|
|
||||||
$(TARGET): $(OBJS) | $(BINDIR)
|
$(TARGET): $(OBJS) | $(BINDIR)
|
||||||
@echo "Linking $@..."
|
@echo "Linking $@..."
|
||||||
@$(CC) $(OBJS) -o $@ $(LDFLAGS)
|
@$(CC) $(OBJS) -o $@ $(LDFLAGS)
|
||||||
@echo "Build complete: $@"
|
@echo "Build complete: $@"
|
||||||
|
|
||||||
# Compile
|
|
||||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
|
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
|
||||||
@echo "Compiling $<..."
|
@echo "Compiling $<..."
|
||||||
@$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
@$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
# Include dependencies
|
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
# Clean build artifacts
|
|
||||||
clean:
|
clean:
|
||||||
@echo "Cleaning..."
|
@echo "Cleaning artifacts..."
|
||||||
@rm -rf $(OBJDIR) $(BINDIR)
|
@rm -rf $(OBJDIR) $(BINDIR)
|
||||||
@echo "Clean complete."
|
|
||||||
|
|
||||||
# Install to system
|
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
install: release
|
install: release
|
||||||
@echo "Installing to $(PREFIX)/bin..."
|
@echo "Deploying to $(PREFIX)/bin..."
|
||||||
@install -d $(PREFIX)/bin
|
@install -d $(PREFIX)/bin
|
||||||
@install -m 755 $(TARGET) $(PREFIX)/bin/
|
@install -m 755 $(TARGET) $(PREFIX)/bin/
|
||||||
@echo "Installation complete."
|
|
||||||
|
|
||||||
# Uninstall from system
|
|
||||||
uninstall:
|
uninstall:
|
||||||
@echo "Uninstalling from $(PREFIX)/bin..."
|
|
||||||
@rm -f $(PREFIX)/bin/ascii3d
|
@rm -f $(PREFIX)/bin/ascii3d
|
||||||
@echo "Uninstallation complete."
|
@echo "Removed $(PREFIX)/bin/ascii3d."
|
||||||
|
|
||||||
# Run the program
|
|
||||||
run: release
|
run: release
|
||||||
@./$(TARGET)
|
@./$(TARGET)
|
||||||
|
|
||||||
# Run with demo text
|
|
||||||
demo: release
|
demo: release
|
||||||
@./$(TARGET) -a HELLO
|
@./$(TARGET) -a HELLO
|
||||||
|
|
||||||
# Static analysis
|
|
||||||
analyze:
|
analyze:
|
||||||
@echo "Running static analysis..."
|
|
||||||
@cppcheck --enable=all --std=c11 -I$(INCDIR) $(SRCDIR)/*.c
|
@cppcheck --enable=all --std=c11 -I$(INCDIR) $(SRCDIR)/*.c
|
||||||
|
|
||||||
# Format code
|
|
||||||
format:
|
format:
|
||||||
@echo "Formatting code..."
|
|
||||||
@clang-format -i $(SRCDIR)/*.c $(INCDIR)/*.h
|
@clang-format -i $(SRCDIR)/*.c $(INCDIR)/*.h
|
||||||
|
|
||||||
# Help
|
|
||||||
help:
|
help:
|
||||||
@echo "ASCII 3D Renderer - Build System"
|
@echo "ASCII 3D Renderer - Build System"
|
||||||
@echo "================================"
|
@echo "================================"
|
||||||
@echo ""
|
@echo "Targets: all (default), release, debug, profile, clean, install, uninstall, run, demo, analyze, format"
|
||||||
@echo "Targets:"
|
|
||||||
@echo " all - Build release version (default)"
|
|
||||||
@echo " release - Build optimized release version"
|
|
||||||
@echo " debug - Build debug version with sanitizers"
|
|
||||||
@echo " profile - Build with profiling support"
|
|
||||||
@echo " clean - Remove build artifacts"
|
|
||||||
@echo " install - Install to system (PREFIX=/usr/local)"
|
|
||||||
@echo " uninstall - Remove from system"
|
|
||||||
@echo " run - Build and run"
|
|
||||||
@echo " demo - Build and run demo"
|
|
||||||
@echo " analyze - Run static analysis (requires cppcheck)"
|
|
||||||
@echo " format - Format source code (requires clang-format)"
|
|
||||||
@echo " help - Show this help"
|
|
||||||
@echo ""
|
|
||||||
@echo "Examples:"
|
|
||||||
@echo " make # Build release"
|
|
||||||
@echo " make debug # Build debug"
|
|
||||||
@echo " make run # Build and run"
|
|
||||||
@echo " make install PREFIX=~ # Install to home directory"
|
|
||||||
|
|||||||
254
README.md
254
README.md
@@ -1,239 +1,59 @@
|
|||||||
# ASCII 3D Renderer v2.0
|
# ASCII 3D Renderer
|
||||||
|
|
||||||
An advanced, high-quality ASCII 3D text renderer written in C for fun.
|
An advanced software-based 3D text renderer pipeline written purely in C11.
|
||||||
|
|
||||||
## Features
|
By avoiding proprietary graphics stacks, this engine demonstrates foundational rendering principles—like Z-buffer processing, Phong illumination models, matrices integration, and anti-aliasing sweeps—executing cleanly inside standard POSIX terminal boundaries.
|
||||||
|
|
||||||
### Rendering
|
## Architecture & Documentation
|
||||||
|
|
||||||
- **Phong Lighting Model** - Realistic lighting with ambient, diffuse, and specular components
|
Review [DOCUMENTATION.md](DOCUMENTATION.md) for deeper knowledge of subsystems, technical boundaries, and lighting logic optimizations employed in the source code.
|
||||||
- **Multiple Light Sources** - Key light, fill light, and rim light for professional 3-point lighting
|
Review [CHANGELOG.md](CHANGELOG.md) for version release tracking constraints.
|
||||||
- **Smooth Normals** - Averaged surface normals for smoother shading on edges
|
|
||||||
- **Multiple Render Modes** - Solid, wireframe, points, and full shaded rendering
|
|
||||||
- **Anti-Aliasing** - Optional sub-pixel sampling for smoother output
|
|
||||||
|
|
||||||
### Visual Quality
|
## Technical Feats
|
||||||
|
|
||||||
- **Extended ASCII Palettes** - 70-level gradient for detailed shading
|
- Real-time `termios`-driven TUI with non-blocking key capture for dynamic typing
|
||||||
- **Block Characters** - Unicode block shading option (░▒▓█)
|
- Mathematically robust 3D normal computations leveraging 2D glyph extrusion passes
|
||||||
- **ANSI Color Support** - 16-color, 256-color, and 24-bit truecolor modes
|
- Configurable AA (multi-sampling) and shading density metrics per-pixel
|
||||||
- **Configurable Quality** - Adjustable voxel density for performance/quality tradeoff
|
- Support for RGB Truecolor processing directly through terminal escape sequences
|
||||||
|
- Strictly isolated C11 components minimizing external dependencies
|
||||||
|
|
||||||
### Technical
|
## Rapid Compilation via Makefile
|
||||||
|
|
||||||
- **Pure C11** - No external graphics dependencies
|
|
||||||
- **60 FPS** - Smooth real-time animation
|
|
||||||
- **Depth Buffering** - Proper 3D occlusion
|
|
||||||
- **Modular Architecture** - Clean separation of concerns
|
|
||||||
- **Production-Ready** - Strict compiler warnings, proper error handling
|
|
||||||
|
|
||||||
## Supported Characters
|
|
||||||
|
|
||||||
- Uppercase letters: `A-Z`
|
|
||||||
- Lowercase letters: `a-z` (rendered as uppercase)
|
|
||||||
- Digits: `0-9`
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- Clang or GCC (C11 support)
|
|
||||||
- GNU Make
|
|
||||||
- POSIX-compliant system (Linux, macOS, WSL)
|
|
||||||
|
|
||||||
### Quick Start
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build release version
|
# Deploys standard -O3 and -flto compiler optimizations
|
||||||
make
|
make release
|
||||||
|
|
||||||
# Run with default text
|
# Run directly, observing standard parameters
|
||||||
./bin/ascii3d
|
./bin/ascii3d
|
||||||
|
|
||||||
# Run with custom text
|
|
||||||
./bin/ascii3d HELLO
|
|
||||||
|
|
||||||
# Truecolor with all-axis rotation
|
|
||||||
./bin/ascii3d -a -c3 WORLD
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build Targets
|
## Interactive TUI Controls
|
||||||
|
|
||||||
|
Upon executing, the renderer spins into an active TTY session:
|
||||||
|
|
||||||
|
- **Type Alphanumeric Characters** – Immediately replaces the internal 3D model with your input in real-time.
|
||||||
|
- `[w]` and `[s]` – Increase / decrease rotational velocity.
|
||||||
|
- `[Space]` – Toggle rotation engine pause constraints.
|
||||||
|
- `[m]` – Cycles between topology rendering (Wireframe -> Shaded Pipeline -> Solid Geometry -> Points).
|
||||||
|
- `[c]` – Switch between rendering spaces (Mono Base -> 16/256 ANSI -> Full Truecolor mapping).
|
||||||
|
- `[p]` – Change ASCII shading dictionaries on the fly.
|
||||||
|
- `[q]` or `ESC` – Safely detaches the IO hooks and shuts the process down cleanly.
|
||||||
|
|
||||||
|
## Execution Options
|
||||||
|
|
||||||
|
Command-line invocations to bypass or configure TUI session states:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make release # Optimized build (default)
|
# Tumbling rotation utilizing Truecolor ANSI standards
|
||||||
make debug # Debug build with sanitizers
|
|
||||||
make profile # Build with profiling support
|
|
||||||
make clean # Remove build artifacts
|
|
||||||
make install # Install to /usr/local/bin
|
|
||||||
make help # Show all targets
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ascii3d [OPTIONS] [TEXT]
|
|
||||||
|
|
||||||
ROTATION OPTIONS:
|
|
||||||
-s <speed> Rotation speed multiplier (default: 1.0)
|
|
||||||
-x Enable X-axis rotation
|
|
||||||
-y Enable Y-axis rotation (default)
|
|
||||||
-z Enable Z-axis rotation
|
|
||||||
-a Enable all axis rotations
|
|
||||||
|
|
||||||
RENDER MODE OPTIONS:
|
|
||||||
-m <mode> Render mode:
|
|
||||||
0 = Solid (filled)
|
|
||||||
1 = Wireframe (edges only)
|
|
||||||
2 = Points (sparse)
|
|
||||||
3 = Shaded (full Phong lighting) [default]
|
|
||||||
|
|
||||||
COLOR OPTIONS:
|
|
||||||
-c <mode> Color mode:
|
|
||||||
0 = Monochrome ASCII [default]
|
|
||||||
1 = 16-color ANSI
|
|
||||||
2 = 256-color ANSI
|
|
||||||
3 = Truecolor (24-bit RGB)
|
|
||||||
|
|
||||||
QUALITY OPTIONS:
|
|
||||||
-q <quality> Render quality (0.5 - 2.0, default: 1.0)
|
|
||||||
-A Enable anti-aliasing
|
|
||||||
-p <palette> Shading palette:
|
|
||||||
0 = Standard (10 levels)
|
|
||||||
1 = Extended (70 levels) [default]
|
|
||||||
2 = Block characters
|
|
||||||
3 = Minimal (6 levels)
|
|
||||||
|
|
||||||
OTHER OPTIONS:
|
|
||||||
-f Show FPS counter
|
|
||||||
-h Show help message
|
|
||||||
```
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Simple Y-axis rotation (default)
|
|
||||||
./bin/ascii3d HELLO
|
|
||||||
|
|
||||||
# Tumbling rotation with truecolor
|
|
||||||
./bin/ascii3d -a -c3 WORLD
|
./bin/ascii3d -a -c3 WORLD
|
||||||
|
|
||||||
# Fast wireframe mode
|
# Quality override with heavy Anti-Aliasing pipeline
|
||||||
./bin/ascii3d -m1 -s2 WIRE
|
|
||||||
|
|
||||||
# High quality with anti-aliasing
|
|
||||||
./bin/ascii3d -A -q1.5 -c2 HQ
|
./bin/ascii3d -A -q1.5 -c2 HQ
|
||||||
|
|
||||||
# Block character style
|
# Show full parameter dictionary
|
||||||
./bin/ascii3d -p2 BLOCKS
|
./bin/ascii3d -h
|
||||||
|
|
||||||
# Show FPS counter
|
|
||||||
./bin/ascii3d -f -a TEST
|
|
||||||
|
|
||||||
# Slow, high quality render
|
|
||||||
./bin/ascii3d -s0.3 -q2 -A SMOOTH
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Press `Ctrl+C` to exit.
|
## Contributing
|
||||||
|
|
||||||
## Project Structure
|
The codebase attempts strict functional modularity. We accept modifications introducing new geometry patterns, deeper PBR emulation methods, or expanded rendering logic assuming complete dependency evasion.
|
||||||
|
|
||||||
```bash
|
|
||||||
ASCIIRenderer/
|
|
||||||
├── include/
|
|
||||||
│ ├── config.h # Configuration and constants
|
|
||||||
│ ├── vec3.h # 3D vector mathematics
|
|
||||||
│ ├── font.h # Bitmap font interface
|
|
||||||
│ ├── lighting.h # Phong lighting system
|
|
||||||
│ ├── renderer.h # Core rendering engine
|
|
||||||
│ └── timing.h # High-precision timing
|
|
||||||
├── src/
|
|
||||||
│ ├── vec3.c # Vector operations
|
|
||||||
│ ├── font.c # 5x7 bitmap font data
|
|
||||||
│ ├── lighting.c # Lighting calculations
|
|
||||||
│ ├── renderer.c # Advanced rendering
|
|
||||||
│ ├── timing.c # Timing utilities
|
|
||||||
│ └── main.c # Entry point and CLI
|
|
||||||
├── Makefile # Build system (Clang)
|
|
||||||
└── README.md # This file
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### Rendering Pipeline
|
|
||||||
|
|
||||||
1. **Font Lookup** - Characters are defined as 5x7 bitmap glyphs
|
|
||||||
2. **3D Extrusion** - Each pixel is extruded along Z-axis to create depth
|
|
||||||
3. **Surface Detection** - Only surface voxels are rendered (optimization)
|
|
||||||
4. **Normal Calculation** - Smooth normals computed from adjacent faces
|
|
||||||
5. **Rotation** - 3D rotation matrices transform points and normals
|
|
||||||
6. **Projection** - Perspective projection maps 3D to 2D screen space
|
|
||||||
7. **Lighting** - Phong model calculates illumination per voxel
|
|
||||||
8. **Depth Test** - Z-buffer ensures correct occlusion
|
|
||||||
9. **Shading** - Intensity mapped to ASCII character from palette
|
|
||||||
10. **Color** - Optional ANSI escape codes for colored output
|
|
||||||
|
|
||||||
### Lighting Model
|
|
||||||
|
|
||||||
The renderer uses a 3-point lighting setup:
|
|
||||||
|
|
||||||
- **Key Light** - Main light from upper-right-front (warm white)
|
|
||||||
- **Fill Light** - Softer light from left (cool blue tint)
|
|
||||||
- **Rim Light** - Back light for edge definition
|
|
||||||
|
|
||||||
Phong components:
|
|
||||||
|
|
||||||
- **Ambient** - Base illumination (15%)
|
|
||||||
- **Diffuse** - Lambertian reflection (70%)
|
|
||||||
- **Specular** - Blinn-Phong highlights (40%, shininess 32)
|
|
||||||
|
|
||||||
### ASCII Shading Palettes
|
|
||||||
|
|
||||||
**Standard (10 levels):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
.:-=+*#%@
|
|
||||||
```
|
|
||||||
|
|
||||||
**Extended (70 levels):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
.'`^",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$
|
|
||||||
```
|
|
||||||
|
|
||||||
**Block (5 levels):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
░▒▓█
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Edit `include/config.h` to customize:
|
|
||||||
|
|
||||||
```c
|
|
||||||
/* Screen dimensions */
|
|
||||||
#define SCREEN_WIDTH 120
|
|
||||||
#define SCREEN_HEIGHT 45
|
|
||||||
|
|
||||||
/* Rendering quality */
|
|
||||||
#define EXTRUSION_DEPTH 4.0f // 3D depth of characters
|
|
||||||
#define CHAR_SCALE 2.0f // Character size
|
|
||||||
#define VOXEL_STEP 0.15f // Voxel density (smaller = higher quality)
|
|
||||||
|
|
||||||
/* Lighting */
|
|
||||||
#define AMBIENT_INTENSITY 0.15f
|
|
||||||
#define DIFFUSE_INTENSITY 0.70f
|
|
||||||
#define SPECULAR_INTENSITY 0.40f
|
|
||||||
#define SPECULAR_POWER 32.0f
|
|
||||||
|
|
||||||
/* Animation */
|
|
||||||
#define TARGET_FPS 60
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
- **60 FPS** on modern hardware
|
|
||||||
- **Surface-only rendering** - Interior voxels skipped
|
|
||||||
- **Efficient depth buffer** - Single-pass rendering
|
|
||||||
- **Minimal memory** - Static buffers, no dynamic allocation
|
|
||||||
- **Quality scaling** - `-q` option for performance tuning
|
|
||||||
|
|||||||
125
include/config.h
125
include/config.h
@@ -1,10 +1,3 @@
|
|||||||
/**
|
|
||||||
* @file config.h
|
|
||||||
* @brief Configuration constants for ASCII 3D Renderer
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 2.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASCII3D_CONFIG_H
|
#ifndef ASCII3D_CONFIG_H
|
||||||
#define ASCII3D_CONFIG_H
|
#define ASCII3D_CONFIG_H
|
||||||
|
|
||||||
@@ -12,159 +5,73 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*============================================================================
|
// Base viewport dimensions. Increase for monolithic monitors.
|
||||||
* SCREEN CONFIGURATION
|
|
||||||
*============================================================================*/
|
|
||||||
#define SCREEN_WIDTH 120
|
#define SCREEN_WIDTH 120
|
||||||
#define SCREEN_HEIGHT 45
|
#define SCREEN_HEIGHT 45
|
||||||
|
|
||||||
/*============================================================================
|
|
||||||
* RENDERING QUALITY SETTINGS
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Depth buffer initialization value */
|
|
||||||
#define DEPTH_BUFFER_INIT 1e9f
|
#define DEPTH_BUFFER_INIT 1e9f
|
||||||
|
|
||||||
/* Character extrusion depth (3D thickness) */
|
|
||||||
#define EXTRUSION_DEPTH 4.0f
|
#define EXTRUSION_DEPTH 4.0f
|
||||||
|
|
||||||
/* Base character scale */
|
|
||||||
#define CHAR_SCALE 2.0f
|
#define CHAR_SCALE 2.0f
|
||||||
|
|
||||||
/* Camera settings */
|
|
||||||
#define CAMERA_DISTANCE 30.0f
|
#define CAMERA_DISTANCE 30.0f
|
||||||
#define FIELD_OF_VIEW 50.0f
|
#define FIELD_OF_VIEW 50.0f
|
||||||
#define NEAR_PLANE 0.1f
|
#define NEAR_PLANE 0.1f
|
||||||
#define FAR_PLANE 100.0f
|
#define FAR_PLANE 100.0f
|
||||||
|
|
||||||
/* Sub-pixel sampling for anti-aliasing (NxN samples per pixel) */
|
// NxN jitter patterns for anti-aliasing passes
|
||||||
#define AA_SAMPLES 2
|
#define AA_SAMPLES 2
|
||||||
|
|
||||||
/* Voxel rendering step (smaller = higher quality, slower) */
|
// Depth steps for voxel ray casting computation
|
||||||
#define VOXEL_STEP 0.15f
|
#define VOXEL_STEP 0.15f
|
||||||
|
|
||||||
/* Surface smoothing iterations */
|
|
||||||
#define SMOOTH_PASSES 1
|
#define SMOOTH_PASSES 1
|
||||||
|
|
||||||
/*============================================================================
|
|
||||||
* ANIMATION PARAMETERS
|
|
||||||
*============================================================================*/
|
|
||||||
#define TARGET_FPS 60
|
#define TARGET_FPS 60
|
||||||
#define FRAME_TIME_US (1000000 / TARGET_FPS)
|
#define FRAME_TIME_US (1000000 / TARGET_FPS)
|
||||||
|
|
||||||
/*============================================================================
|
|
||||||
* FONT CONFIGURATION
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Standard 5x7 font */
|
|
||||||
#define FONT_WIDTH 5
|
#define FONT_WIDTH 5
|
||||||
#define FONT_HEIGHT 7
|
#define FONT_HEIGHT 7
|
||||||
#define FONT_CHAR_SPACING 2
|
#define FONT_CHAR_SPACING 2
|
||||||
|
|
||||||
/*============================================================================
|
// Physics-Based lighting configurations
|
||||||
* LIGHTING CONFIGURATION
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Ambient light intensity (0.0 - 1.0) */
|
|
||||||
#define AMBIENT_INTENSITY 0.15f
|
#define AMBIENT_INTENSITY 0.15f
|
||||||
|
|
||||||
/* Diffuse light intensity */
|
|
||||||
#define DIFFUSE_INTENSITY 0.70f
|
#define DIFFUSE_INTENSITY 0.70f
|
||||||
|
|
||||||
/* Specular light intensity */
|
|
||||||
#define SPECULAR_INTENSITY 0.40f
|
#define SPECULAR_INTENSITY 0.40f
|
||||||
|
|
||||||
/* Specular shininess exponent */
|
|
||||||
#define SPECULAR_POWER 32.0f
|
#define SPECULAR_POWER 32.0f
|
||||||
|
|
||||||
/* Number of light sources */
|
|
||||||
#define MAX_LIGHTS 3
|
#define MAX_LIGHTS 3
|
||||||
|
|
||||||
/*============================================================================
|
// Shader maps determining intensity mappings
|
||||||
* ASCII SHADING PALETTES
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Standard gradient (10 levels) */
|
|
||||||
#define SHADE_CHARS_STANDARD " .:-=+*#%@"
|
#define SHADE_CHARS_STANDARD " .:-=+*#%@"
|
||||||
#define SHADE_COUNT_STANDARD 10
|
#define SHADE_COUNT_STANDARD 10
|
||||||
|
#define SHADE_CHARS_EXTENDED \
|
||||||
/* Extended gradient (16 levels) - more detail */
|
" .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
|
||||||
#define SHADE_CHARS_EXTENDED " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
|
|
||||||
#define SHADE_COUNT_EXTENDED 70
|
#define SHADE_COUNT_EXTENDED 70
|
||||||
|
|
||||||
/* Block characters for solid look */
|
|
||||||
#define SHADE_CHARS_BLOCK " ░▒▓█"
|
#define SHADE_CHARS_BLOCK " ░▒▓█"
|
||||||
#define SHADE_COUNT_BLOCK 5
|
#define SHADE_COUNT_BLOCK 5
|
||||||
|
|
||||||
/* Minimal gradient */
|
|
||||||
#define SHADE_CHARS_MINIMAL " .:+#@"
|
#define SHADE_CHARS_MINIMAL " .:+#@"
|
||||||
#define SHADE_COUNT_MINIMAL 6
|
#define SHADE_COUNT_MINIMAL 6
|
||||||
|
|
||||||
/* Default palette */
|
|
||||||
#define SHADE_CHARS SHADE_CHARS_EXTENDED
|
#define SHADE_CHARS SHADE_CHARS_EXTENDED
|
||||||
#define SHADE_COUNT SHADE_COUNT_EXTENDED
|
#define SHADE_COUNT SHADE_COUNT_EXTENDED
|
||||||
|
|
||||||
/*============================================================================
|
|
||||||
* RENDER MODES
|
|
||||||
*============================================================================*/
|
|
||||||
typedef enum RenderMode {
|
typedef enum RenderMode {
|
||||||
RENDER_MODE_SOLID = 0, /* Filled solid rendering */
|
RENDER_MODE_SOLID = 0,
|
||||||
RENDER_MODE_WIREFRAME, /* Edge-only wireframe */
|
RENDER_MODE_WIREFRAME,
|
||||||
RENDER_MODE_POINTS, /* Point cloud */
|
RENDER_MODE_POINTS,
|
||||||
RENDER_MODE_SHADED, /* Full Phong shading */
|
RENDER_MODE_SHADED,
|
||||||
RENDER_MODE_COUNT
|
RENDER_MODE_COUNT
|
||||||
} RenderMode;
|
} RenderMode;
|
||||||
|
|
||||||
/*============================================================================
|
|
||||||
* COLOR MODES
|
|
||||||
*============================================================================*/
|
|
||||||
typedef enum ColorMode {
|
typedef enum ColorMode {
|
||||||
COLOR_MODE_MONO = 0, /* Monochrome ASCII */
|
COLOR_MODE_MONO = 0,
|
||||||
COLOR_MODE_ANSI_16, /* 16-color ANSI */
|
COLOR_MODE_ANSI_16,
|
||||||
COLOR_MODE_ANSI_256, /* 256-color ANSI */
|
COLOR_MODE_ANSI_256,
|
||||||
COLOR_MODE_TRUECOLOR, /* 24-bit RGB */
|
COLOR_MODE_TRUECOLOR,
|
||||||
COLOR_MODE_COUNT
|
COLOR_MODE_COUNT
|
||||||
} ColorMode;
|
} ColorMode;
|
||||||
|
|
||||||
/*============================================================================
|
|
||||||
* ANSI COLOR CODES
|
|
||||||
*============================================================================*/
|
|
||||||
#define ANSI_RESET "\033[0m"
|
#define ANSI_RESET "\033[0m"
|
||||||
#define ANSI_BOLD "\033[1m"
|
|
||||||
#define ANSI_DIM "\033[2m"
|
|
||||||
|
|
||||||
/* Foreground colors */
|
|
||||||
#define ANSI_FG_BLACK "\033[30m"
|
|
||||||
#define ANSI_FG_RED "\033[31m"
|
|
||||||
#define ANSI_FG_GREEN "\033[32m"
|
|
||||||
#define ANSI_FG_YELLOW "\033[33m"
|
|
||||||
#define ANSI_FG_BLUE "\033[34m"
|
|
||||||
#define ANSI_FG_MAGENTA "\033[35m"
|
|
||||||
#define ANSI_FG_CYAN "\033[36m"
|
|
||||||
#define ANSI_FG_WHITE "\033[37m"
|
|
||||||
|
|
||||||
/* Bright foreground colors */
|
|
||||||
#define ANSI_FG_BRIGHT_BLACK "\033[90m"
|
|
||||||
#define ANSI_FG_BRIGHT_RED "\033[91m"
|
|
||||||
#define ANSI_FG_BRIGHT_GREEN "\033[92m"
|
|
||||||
#define ANSI_FG_BRIGHT_YELLOW "\033[93m"
|
|
||||||
#define ANSI_FG_BRIGHT_BLUE "\033[94m"
|
|
||||||
#define ANSI_FG_BRIGHT_MAGENTA "\033[95m"
|
|
||||||
#define ANSI_FG_BRIGHT_CYAN "\033[96m"
|
|
||||||
#define ANSI_FG_BRIGHT_WHITE "\033[97m"
|
|
||||||
|
|
||||||
/* Background colors */
|
|
||||||
#define ANSI_BG_BLACK "\033[40m"
|
|
||||||
#define ANSI_BG_RED "\033[41m"
|
|
||||||
#define ANSI_BG_GREEN "\033[42m"
|
|
||||||
#define ANSI_BG_YELLOW "\033[43m"
|
|
||||||
#define ANSI_BG_BLUE "\033[44m"
|
|
||||||
#define ANSI_BG_MAGENTA "\033[45m"
|
|
||||||
#define ANSI_BG_CYAN "\033[46m"
|
|
||||||
#define ANSI_BG_WHITE "\033[47m"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ASCII3D_CONFIG_H */
|
#endif
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
/**
|
|
||||||
* @file font.h
|
|
||||||
* @brief Bitmap font data for ASCII 3D Renderer
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASCII3D_FONT_H
|
#ifndef ASCII3D_FONT_H
|
||||||
#define ASCII3D_FONT_H
|
#define ASCII3D_FONT_H
|
||||||
|
|
||||||
@@ -14,31 +7,18 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
// Returns a direct pointer to the 7-byte glyph row data if the char is present
|
||||||
* @brief Get the font glyph data for a character
|
// in our sprite sheet
|
||||||
* @param c Character to look up (A-Z, a-z, 0-9)
|
|
||||||
* @return Pointer to 7-byte glyph data, or NULL if not found
|
|
||||||
*/
|
|
||||||
const unsigned char *font_get_glyph(char c);
|
const unsigned char *font_get_glyph(char c);
|
||||||
|
|
||||||
/**
|
// Validates the bitmap bounds and extracts a specific pixel bit
|
||||||
* @brief Check if a pixel is set in a glyph
|
|
||||||
* @param glyph Pointer to glyph data
|
|
||||||
* @param x X coordinate (0-4)
|
|
||||||
* @param y Y coordinate (0-6)
|
|
||||||
* @return true if pixel is set, false otherwise
|
|
||||||
*/
|
|
||||||
bool font_pixel_set(const unsigned char *glyph, int x, int y);
|
bool font_pixel_set(const unsigned char *glyph, int x, int y);
|
||||||
|
|
||||||
/**
|
// Tests an ASCII character against our mapped glyph dictionary
|
||||||
* @brief Check if character is renderable
|
|
||||||
* @param c Character to check
|
|
||||||
* @return true if character can be rendered
|
|
||||||
*/
|
|
||||||
bool font_is_renderable(char c);
|
bool font_is_renderable(char c);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ASCII3D_FONT_H */
|
#endif
|
||||||
|
|||||||
@@ -1,65 +1,45 @@
|
|||||||
/**
|
|
||||||
* @file lighting.h
|
|
||||||
* @brief Advanced lighting system for ASCII 3D Renderer
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 2.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASCII3D_LIGHTING_H
|
#ifndef ASCII3D_LIGHTING_H
|
||||||
#define ASCII3D_LIGHTING_H
|
#define ASCII3D_LIGHTING_H
|
||||||
|
|
||||||
#include "vec3.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "vec3.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Light types
|
|
||||||
*/
|
|
||||||
typedef enum LightType {
|
typedef enum LightType {
|
||||||
LIGHT_DIRECTIONAL = 0, /* Parallel rays (sun-like) */
|
LIGHT_DIRECTIONAL = 0,
|
||||||
LIGHT_POINT, /* Point source with falloff */
|
LIGHT_POINT,
|
||||||
LIGHT_SPOT /* Cone-shaped spotlight */
|
LIGHT_SPOT
|
||||||
} LightType;
|
} LightType;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief RGB Color structure
|
|
||||||
*/
|
|
||||||
typedef struct Color {
|
typedef struct Color {
|
||||||
float r, g, b;
|
float r, g, b;
|
||||||
} Color;
|
} Color;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Light source structure
|
|
||||||
*/
|
|
||||||
typedef struct Light {
|
typedef struct Light {
|
||||||
LightType type;
|
LightType type;
|
||||||
Vec3 position; /* Position for point/spot lights */
|
Vec3 position;
|
||||||
Vec3 direction; /* Direction for directional/spot lights */
|
Vec3 direction;
|
||||||
Color color; /* Light color */
|
Color color;
|
||||||
float intensity; /* Light intensity multiplier */
|
float intensity;
|
||||||
float falloff; /* Attenuation for point lights */
|
float falloff;
|
||||||
float spot_angle; /* Cone angle for spotlights (radians) */
|
float spot_angle;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
} Light;
|
} Light;
|
||||||
|
|
||||||
/**
|
// PBR surface components
|
||||||
* @brief Material properties for surfaces
|
|
||||||
*/
|
|
||||||
typedef struct Material {
|
typedef struct Material {
|
||||||
Color ambient; /* Ambient color */
|
Color ambient;
|
||||||
Color diffuse; /* Diffuse color */
|
Color diffuse;
|
||||||
Color specular; /* Specular highlight color */
|
Color specular;
|
||||||
float shininess; /* Specular exponent */
|
float shininess;
|
||||||
float reflectivity; /* For future use */
|
float reflectivity;
|
||||||
} Material;
|
} Material;
|
||||||
|
|
||||||
/**
|
// Encapsulates the entire render context's virtual ecosystem
|
||||||
* @brief Lighting system state
|
|
||||||
*/
|
|
||||||
typedef struct LightingSystem {
|
typedef struct LightingSystem {
|
||||||
Light lights[MAX_LIGHTS];
|
Light lights[MAX_LIGHTS];
|
||||||
int light_count;
|
int light_count;
|
||||||
@@ -68,103 +48,34 @@ typedef struct LightingSystem {
|
|||||||
Vec3 camera_position;
|
Vec3 camera_position;
|
||||||
} LightingSystem;
|
} LightingSystem;
|
||||||
|
|
||||||
/**
|
// Instantiates default 3-point portrait lighting scheme
|
||||||
* @brief Initialize the lighting system with default lights
|
|
||||||
* @param system Lighting system to initialize
|
|
||||||
*/
|
|
||||||
void lighting_init(LightingSystem *system);
|
void lighting_init(LightingSystem *system);
|
||||||
|
|
||||||
/**
|
// Registers a new light emitter into the system context
|
||||||
* @brief Add a light to the system
|
|
||||||
* @param system Lighting system
|
|
||||||
* @param light Light to add
|
|
||||||
* @return Index of added light, or -1 if full
|
|
||||||
*/
|
|
||||||
int lighting_add_light(LightingSystem *system, const Light *light);
|
int lighting_add_light(LightingSystem *system, const Light *light);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a directional light
|
|
||||||
* @param direction Light direction (will be normalized)
|
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
* @return Configured light structure
|
|
||||||
*/
|
|
||||||
Light lighting_create_directional(Vec3 direction, Color color, float intensity);
|
Light lighting_create_directional(Vec3 direction, Color color, float intensity);
|
||||||
|
Light lighting_create_point(Vec3 position, Color color, float intensity,
|
||||||
|
float falloff);
|
||||||
|
|
||||||
/**
|
// Computes monochrome or RGB shading based on surface interactions
|
||||||
* @brief Create a point light
|
float lighting_calculate(const LightingSystem *system, Vec3 point, Vec3 normal,
|
||||||
* @param position Light position
|
const Material *material);
|
||||||
* @param color Light color
|
|
||||||
* @param intensity Light intensity
|
|
||||||
* @param falloff Attenuation factor
|
|
||||||
* @return Configured light structure
|
|
||||||
*/
|
|
||||||
Light lighting_create_point(Vec3 position, Color color, float intensity, float falloff);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate lighting at a surface point (Phong model)
|
|
||||||
* @param system Lighting system
|
|
||||||
* @param point Surface point position
|
|
||||||
* @param normal Surface normal (must be normalized)
|
|
||||||
* @param material Surface material
|
|
||||||
* @return Final illumination value (0.0 - 1.0+)
|
|
||||||
*/
|
|
||||||
float lighting_calculate(const LightingSystem *system, Vec3 point,
|
|
||||||
Vec3 normal, const Material *material);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate full color lighting
|
|
||||||
* @param system Lighting system
|
|
||||||
* @param point Surface point position
|
|
||||||
* @param normal Surface normal
|
|
||||||
* @param material Surface material
|
|
||||||
* @return Final color
|
|
||||||
*/
|
|
||||||
Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
||||||
Vec3 normal, const Material *material);
|
Vec3 normal, const Material *material);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create default material
|
|
||||||
* @return Default white material
|
|
||||||
*/
|
|
||||||
Material lighting_default_material(void);
|
Material lighting_default_material(void);
|
||||||
|
|
||||||
/**
|
// Floating point color combinators
|
||||||
* @brief Create a color
|
|
||||||
* @param r Red (0-1)
|
|
||||||
* @param g Green (0-1)
|
|
||||||
* @param b Blue (0-1)
|
|
||||||
* @return Color structure
|
|
||||||
*/
|
|
||||||
Color color_create(float r, float g, float b);
|
Color color_create(float r, float g, float b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Multiply color by scalar
|
|
||||||
*/
|
|
||||||
Color color_scale(Color c, float s);
|
Color color_scale(Color c, float s);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add two colors
|
|
||||||
*/
|
|
||||||
Color color_add(Color a, Color b);
|
Color color_add(Color a, Color b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Multiply two colors component-wise
|
|
||||||
*/
|
|
||||||
Color color_multiply(Color a, Color b);
|
Color color_multiply(Color a, Color b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clamp color components to 0-1 range
|
|
||||||
*/
|
|
||||||
Color color_clamp(Color c);
|
Color color_clamp(Color c);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convert color to grayscale intensity
|
|
||||||
*/
|
|
||||||
float color_to_intensity(Color c);
|
float color_to_intensity(Color c);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ASCII3D_LIGHTING_H */
|
#endif
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
/**
|
|
||||||
* @file renderer.h
|
|
||||||
* @brief Advanced rendering engine for ASCII 3D Renderer
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 2.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASCII3D_RENDERER_H
|
#ifndef ASCII3D_RENDERER_H
|
||||||
#define ASCII3D_RENDERER_H
|
#define ASCII3D_RENDERER_H
|
||||||
|
|
||||||
@@ -16,9 +9,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Rotation state for animation
|
|
||||||
*/
|
|
||||||
typedef struct RotationState {
|
typedef struct RotationState {
|
||||||
float angle_x;
|
float angle_x;
|
||||||
float angle_y;
|
float angle_y;
|
||||||
@@ -29,151 +19,63 @@ typedef struct RotationState {
|
|||||||
float speed;
|
float speed;
|
||||||
} RotationState;
|
} RotationState;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render settings structure
|
|
||||||
*/
|
|
||||||
typedef struct RenderSettings {
|
typedef struct RenderSettings {
|
||||||
RenderMode mode; /* Rendering mode */
|
RenderMode mode;
|
||||||
ColorMode color_mode; /* Color output mode */
|
ColorMode color_mode;
|
||||||
bool anti_aliasing; /* Enable AA */
|
bool anti_aliasing;
|
||||||
bool show_fps; /* Display FPS counter */
|
bool show_fps;
|
||||||
bool auto_rotate; /* Auto rotation enabled */
|
bool auto_rotate;
|
||||||
float quality; /* Quality multiplier (0.5 - 2.0) */
|
float quality;
|
||||||
int palette_index; /* Shading palette selection */
|
int palette_index;
|
||||||
Color base_color; /* Base color for colored modes */
|
Color base_color;
|
||||||
Color highlight_color; /* Highlight/specular color */
|
Color highlight_color;
|
||||||
} RenderSettings;
|
} RenderSettings;
|
||||||
|
|
||||||
/**
|
// Diagnostics profile updated each tick
|
||||||
* @brief Frame statistics
|
|
||||||
*/
|
|
||||||
typedef struct FrameStats {
|
typedef struct FrameStats {
|
||||||
double frame_time; /* Last frame time in ms */
|
double frame_time;
|
||||||
double fps; /* Current FPS */
|
double fps;
|
||||||
double avg_fps; /* Average FPS */
|
double avg_fps;
|
||||||
int frame_count; /* Total frames rendered */
|
int frame_count;
|
||||||
int triangles; /* Triangles/voxels rendered */
|
int triangles;
|
||||||
} FrameStats;
|
} FrameStats;
|
||||||
|
|
||||||
/**
|
// Core Engine Lifecycles
|
||||||
* @brief Initialize the renderer
|
|
||||||
* @return 0 on success, -1 on failure
|
|
||||||
*/
|
|
||||||
int renderer_init(void);
|
int renderer_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Cleanup renderer resources
|
|
||||||
*/
|
|
||||||
void renderer_cleanup(void);
|
void renderer_cleanup(void);
|
||||||
|
|
||||||
/**
|
// Nulls out all raster buffers ready for drawing
|
||||||
* @brief Clear the screen and depth buffers
|
|
||||||
*/
|
|
||||||
void renderer_clear(void);
|
void renderer_clear(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render a 3D text string
|
|
||||||
* @param text Text to render (A-Z, 0-9)
|
|
||||||
* @param rotation Current rotation state
|
|
||||||
*/
|
|
||||||
void renderer_draw_text(const char *text, const RotationState *rotation);
|
void renderer_draw_text(const char *text, const RotationState *rotation);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render with full settings control
|
|
||||||
* @param text Text to render
|
|
||||||
* @param rotation Rotation state
|
|
||||||
* @param settings Render settings
|
|
||||||
*/
|
|
||||||
void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
||||||
const RenderSettings *settings);
|
const RenderSettings *settings);
|
||||||
|
|
||||||
/**
|
// Flushes the rendering pipeline out to standard output
|
||||||
* @brief Display the rendered frame to terminal
|
|
||||||
*/
|
|
||||||
void renderer_present(void);
|
void renderer_present(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Present with color support
|
|
||||||
* @param settings Render settings for color mode
|
|
||||||
*/
|
|
||||||
void renderer_present_color(const RenderSettings *settings);
|
void renderer_present_color(const RenderSettings *settings);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update rotation angles based on time delta
|
|
||||||
* @param rotation Rotation state to update
|
|
||||||
* @param delta_time Time since last update in seconds
|
|
||||||
*/
|
|
||||||
void renderer_update_rotation(RotationState *rotation, double delta_time);
|
void renderer_update_rotation(RotationState *rotation, double delta_time);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create default rotation state
|
|
||||||
* @return Default rotation state with Y-axis rotation enabled
|
|
||||||
*/
|
|
||||||
RotationState renderer_default_rotation(void);
|
RotationState renderer_default_rotation(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create default render settings
|
|
||||||
* @return Default settings
|
|
||||||
*/
|
|
||||||
RenderSettings renderer_default_settings(void);
|
RenderSettings renderer_default_settings(void);
|
||||||
|
|
||||||
/**
|
// Dynamic rendering reconfiguration toggles
|
||||||
* @brief Set the render mode
|
|
||||||
* @param mode New render mode
|
|
||||||
*/
|
|
||||||
void renderer_set_mode(RenderMode mode);
|
void renderer_set_mode(RenderMode mode);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the color mode
|
|
||||||
* @param mode New color mode
|
|
||||||
*/
|
|
||||||
void renderer_set_color_mode(ColorMode mode);
|
void renderer_set_color_mode(ColorMode mode);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get current frame statistics
|
|
||||||
* @return Frame stats structure
|
|
||||||
*/
|
|
||||||
FrameStats renderer_get_stats(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the shading palette
|
|
||||||
* @param palette_index 0=standard, 1=extended, 2=block, 3=minimal
|
|
||||||
*/
|
|
||||||
void renderer_set_palette(int palette_index);
|
void renderer_set_palette(int palette_index);
|
||||||
|
|
||||||
/**
|
FrameStats renderer_get_stats(void);
|
||||||
* @brief Get the lighting system for modification
|
|
||||||
* @return Pointer to lighting system
|
|
||||||
*/
|
|
||||||
LightingSystem *renderer_get_lighting(void);
|
LightingSystem *renderer_get_lighting(void);
|
||||||
|
|
||||||
/**
|
// Console visual hacks
|
||||||
* @brief Hide terminal cursor
|
|
||||||
*/
|
|
||||||
void renderer_hide_cursor(void);
|
void renderer_hide_cursor(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Show terminal cursor
|
|
||||||
*/
|
|
||||||
void renderer_show_cursor(void);
|
void renderer_show_cursor(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear terminal screen
|
|
||||||
*/
|
|
||||||
void renderer_clear_terminal(void);
|
void renderer_clear_terminal(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set terminal to alternate screen buffer
|
|
||||||
*/
|
|
||||||
void renderer_enter_alternate_screen(void);
|
void renderer_enter_alternate_screen(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Restore terminal to main screen buffer
|
|
||||||
*/
|
|
||||||
void renderer_exit_alternate_screen(void);
|
void renderer_exit_alternate_screen(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ASCII3D_RENDERER_H */
|
#endif
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
/**
|
|
||||||
* @file timing.h
|
|
||||||
* @brief High-precision timing utilities
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASCII3D_TIMING_H
|
#ifndef ASCII3D_TIMING_H
|
||||||
#define ASCII3D_TIMING_H
|
#define ASCII3D_TIMING_H
|
||||||
|
|
||||||
@@ -12,27 +5,18 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
// Returns the monotonic wall clock time in seconds, suitable for frame timing.
|
||||||
* @brief Get current time in seconds (monotonic clock)
|
|
||||||
* @return Time in seconds with nanosecond precision
|
|
||||||
*/
|
|
||||||
double timing_get_seconds(void);
|
double timing_get_seconds(void);
|
||||||
|
|
||||||
/**
|
// Microsecond-scale thread suspension layer.
|
||||||
* @brief Sleep for specified microseconds
|
|
||||||
* @param microseconds Duration to sleep
|
|
||||||
*/
|
|
||||||
void timing_sleep_us(unsigned int microseconds);
|
void timing_sleep_us(unsigned int microseconds);
|
||||||
|
|
||||||
/**
|
// Calculates dynamic sleep padding against a target FPS to prevent high CPU
|
||||||
* @brief Frame rate limiter - sleeps to maintain target FPS
|
// utilization
|
||||||
* @param frame_start_time Time when frame started (from timing_get_seconds)
|
|
||||||
* @param target_fps Target frames per second
|
|
||||||
*/
|
|
||||||
void timing_limit_fps(double frame_start_time, int target_fps);
|
void timing_limit_fps(double frame_start_time, int target_fps);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ASCII3D_TIMING_H */
|
#endif
|
||||||
|
|||||||
29
include/tui.h
Normal file
29
include/tui.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef ASCII3D_TUI_H
|
||||||
|
#define ASCII3D_TUI_H
|
||||||
|
|
||||||
|
#include "renderer.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Reconfigures standard input to be non-blocking and disables canonical mode.
|
||||||
|
// This allows us to poll the keyboard gracefully in the render loop.
|
||||||
|
int tui_init(void);
|
||||||
|
|
||||||
|
// Restores terminal to its initial state to avoid leaving users with a broken
|
||||||
|
// prompt.
|
||||||
|
void tui_cleanup(void);
|
||||||
|
|
||||||
|
// Processes pending keys and updates application state.
|
||||||
|
// Standard typing appends to the text_buffer, while specific commands alter
|
||||||
|
// settings. Returns non-zero when an exit is requested.
|
||||||
|
int tui_process_input(RotationState *rotation, RenderSettings *settings,
|
||||||
|
char *text_buffer, size_t max_text_len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,10 +1,3 @@
|
|||||||
/**
|
|
||||||
* @file vec3.h
|
|
||||||
* @brief 3D Vector mathematics for ASCII 3D Renderer
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ASCII3D_VEC3_H
|
#ifndef ASCII3D_VEC3_H
|
||||||
#define ASCII3D_VEC3_H
|
#define ASCII3D_VEC3_H
|
||||||
|
|
||||||
@@ -12,104 +5,29 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
// A standard 3-component float vector
|
||||||
* @brief 3D Vector structure
|
|
||||||
*/
|
|
||||||
typedef struct Vec3 {
|
typedef struct Vec3 {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float z;
|
float z;
|
||||||
} Vec3;
|
} Vec3;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create a new Vec3
|
|
||||||
* @param x X component
|
|
||||||
* @param y Y component
|
|
||||||
* @param z Z component
|
|
||||||
* @return New Vec3 instance
|
|
||||||
*/
|
|
||||||
Vec3 vec3_create(float x, float y, float z);
|
Vec3 vec3_create(float x, float y, float z);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add two vectors
|
|
||||||
* @param a First vector
|
|
||||||
* @param b Second vector
|
|
||||||
* @return Result of a + b
|
|
||||||
*/
|
|
||||||
Vec3 vec3_add(Vec3 a, Vec3 b);
|
Vec3 vec3_add(Vec3 a, Vec3 b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Subtract two vectors
|
|
||||||
* @param a First vector
|
|
||||||
* @param b Second vector
|
|
||||||
* @return Result of a - b
|
|
||||||
*/
|
|
||||||
Vec3 vec3_sub(Vec3 a, Vec3 b);
|
Vec3 vec3_sub(Vec3 a, Vec3 b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Scale a vector by a scalar
|
|
||||||
* @param v Vector to scale
|
|
||||||
* @param s Scalar value
|
|
||||||
* @return Scaled vector
|
|
||||||
*/
|
|
||||||
Vec3 vec3_scale(Vec3 v, float s);
|
Vec3 vec3_scale(Vec3 v, float s);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate dot product of two vectors
|
|
||||||
* @param a First vector
|
|
||||||
* @param b Second vector
|
|
||||||
* @return Dot product
|
|
||||||
*/
|
|
||||||
float vec3_dot(Vec3 a, Vec3 b);
|
float vec3_dot(Vec3 a, Vec3 b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate cross product of two vectors
|
|
||||||
* @param a First vector
|
|
||||||
* @param b Second vector
|
|
||||||
* @return Cross product
|
|
||||||
*/
|
|
||||||
Vec3 vec3_cross(Vec3 a, Vec3 b);
|
Vec3 vec3_cross(Vec3 a, Vec3 b);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculate length of a vector
|
|
||||||
* @param v Vector
|
|
||||||
* @return Length
|
|
||||||
*/
|
|
||||||
float vec3_length(Vec3 v);
|
float vec3_length(Vec3 v);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Normalize a vector
|
|
||||||
* @param v Vector to normalize
|
|
||||||
* @return Normalized vector
|
|
||||||
*/
|
|
||||||
Vec3 vec3_normalize(Vec3 v);
|
Vec3 vec3_normalize(Vec3 v);
|
||||||
|
|
||||||
/**
|
// Rotating points through standard Euler coordinate transformations
|
||||||
* @brief Rotate vector around X axis
|
|
||||||
* @param v Vector to rotate
|
|
||||||
* @param angle Angle in radians
|
|
||||||
* @return Rotated vector
|
|
||||||
*/
|
|
||||||
Vec3 vec3_rotate_x(Vec3 v, float angle);
|
Vec3 vec3_rotate_x(Vec3 v, float angle);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Rotate vector around Y axis
|
|
||||||
* @param v Vector to rotate
|
|
||||||
* @param angle Angle in radians
|
|
||||||
* @return Rotated vector
|
|
||||||
*/
|
|
||||||
Vec3 vec3_rotate_y(Vec3 v, float angle);
|
Vec3 vec3_rotate_y(Vec3 v, float angle);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Rotate vector around Z axis
|
|
||||||
* @param v Vector to rotate
|
|
||||||
* @param angle Angle in radians
|
|
||||||
* @return Rotated vector
|
|
||||||
*/
|
|
||||||
Vec3 vec3_rotate_z(Vec3 v, float angle);
|
Vec3 vec3_rotate_z(Vec3 v, float angle);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ASCII3D_VEC3_H */
|
#endif
|
||||||
|
|||||||
130
src/font.c
130
src/font.c
@@ -1,105 +1,79 @@
|
|||||||
/**
|
|
||||||
* @file font.c
|
|
||||||
* @brief Bitmap font data implementation
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 1.0.0
|
|
||||||
*
|
|
||||||
* Contains 5x7 bitmap font data for A-Z and 0-9
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
/**
|
// 5x7 ASCII grid font map.
|
||||||
* @brief 5x7 bitmap font data
|
// Each bitmaps uses 7 bytes representing rows. Bits 4 through 0 denote the set
|
||||||
* Each character is represented by 7 bytes (rows)
|
// pixels.
|
||||||
* Each byte contains 5 bits (columns), MSB = leftmost pixel
|
|
||||||
*/
|
|
||||||
static const unsigned char g_font_data[][FONT_HEIGHT] = {
|
static const unsigned char g_font_data[][FONT_HEIGHT] = {
|
||||||
/* A */ {0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11},
|
// A -> Z
|
||||||
/* B */ {0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E},
|
{0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, // A
|
||||||
/* C */ {0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E},
|
{0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E},
|
||||||
/* D */ {0x1C, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1C},
|
{0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E},
|
||||||
/* E */ {0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F},
|
{0x1C, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1C},
|
||||||
/* F */ {0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10},
|
{0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F}, // E
|
||||||
/* G */ {0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0F},
|
{0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10},
|
||||||
/* H */ {0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11},
|
{0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0F},
|
||||||
/* I */ {0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E},
|
{0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11},
|
||||||
/* J */ {0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C},
|
{0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E}, // I
|
||||||
/* K */ {0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11},
|
{0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C},
|
||||||
/* L */ {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F},
|
{0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11},
|
||||||
/* M */ {0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11},
|
{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F},
|
||||||
/* N */ {0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11},
|
{0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11}, // M
|
||||||
/* O */ {0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E},
|
{0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11},
|
||||||
/* P */ {0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10},
|
{0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E},
|
||||||
/* Q */ {0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D},
|
{0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10},
|
||||||
/* R */ {0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11},
|
{0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D}, // Q
|
||||||
/* S */ {0x0E, 0x11, 0x10, 0x0E, 0x01, 0x11, 0x0E},
|
{0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11},
|
||||||
/* T */ {0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04},
|
{0x0E, 0x11, 0x10, 0x0E, 0x01, 0x11, 0x0E},
|
||||||
/* U */ {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E},
|
{0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04},
|
||||||
/* V */ {0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04},
|
{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, // U
|
||||||
/* W */ {0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A},
|
{0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04},
|
||||||
/* X */ {0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11},
|
{0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A},
|
||||||
/* Y */ {0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04},
|
{0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11},
|
||||||
/* Z */ {0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F},
|
{0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04}, // Y
|
||||||
/* 0 */ {0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E},
|
{0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F}, // Z
|
||||||
/* 1 */ {0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E},
|
// 0 -> 9
|
||||||
/* 2 */ {0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F},
|
{0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E}, // 0
|
||||||
/* 3 */ {0x0E, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0E},
|
{0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E},
|
||||||
/* 4 */ {0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02},
|
{0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F},
|
||||||
/* 5 */ {0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E},
|
{0x0E, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0E},
|
||||||
/* 6 */ {0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E},
|
{0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02}, // 4
|
||||||
/* 7 */ {0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08},
|
{0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E},
|
||||||
/* 8 */ {0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E},
|
{0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E},
|
||||||
/* 9 */ {0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C},
|
{0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08},
|
||||||
|
{0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E},
|
||||||
|
{0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C}, // 9
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FONT_LETTER_COUNT 26
|
#define FONT_LETTER_COUNT 26
|
||||||
#define FONT_DIGIT_COUNT 10
|
#define FONT_DIGIT_COUNT 10
|
||||||
|
|
||||||
/**
|
static int font_get_index(char c) {
|
||||||
* @brief Get font index for a character
|
if (c >= 'A' && c <= 'Z')
|
||||||
* @param c Character to look up
|
|
||||||
* @return Index into font data, or -1 if not found
|
|
||||||
*/
|
|
||||||
static int font_get_index(char c)
|
|
||||||
{
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
|
||||||
return c - 'A';
|
return c - 'A';
|
||||||
}
|
if (c >= 'a' && c <= 'z')
|
||||||
if (c >= 'a' && c <= 'z') {
|
|
||||||
return c - 'a';
|
return c - 'a';
|
||||||
}
|
if (c >= '0' && c <= '9')
|
||||||
if (c >= '0' && c <= '9') {
|
|
||||||
return FONT_LETTER_COUNT + (c - '0');
|
return FONT_LETTER_COUNT + (c - '0');
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char *font_get_glyph(char c)
|
const unsigned char *font_get_glyph(char c) {
|
||||||
{
|
|
||||||
int index = font_get_index(c);
|
int index = font_get_index(c);
|
||||||
if (index < 0) {
|
if (index < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
return g_font_data[index];
|
return g_font_data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool font_pixel_set(const unsigned char *glyph, int x, int y)
|
bool font_pixel_set(const unsigned char *glyph, int x, int y) {
|
||||||
{
|
if (glyph == NULL)
|
||||||
if (glyph == NULL) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
if (x < 0 || x >= FONT_WIDTH || y < 0 || y >= FONT_HEIGHT)
|
||||||
if (x < 0 || x >= FONT_WIDTH || y < 0 || y >= FONT_HEIGHT) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
// Map bit flags directly onto screen pixel planes
|
||||||
/* Bit 4 is leftmost (x=0), bit 0 is rightmost (x=4) */
|
|
||||||
return (glyph[y] & (1 << (FONT_WIDTH - 1 - x))) != 0;
|
return (glyph[y] & (1 << (FONT_WIDTH - 1 - x))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool font_is_renderable(char c)
|
bool font_is_renderable(char c) { return font_get_index(c) >= 0; }
|
||||||
{
|
|
||||||
return font_get_index(c) >= 0;
|
|
||||||
}
|
|
||||||
|
|||||||
173
src/lighting.c
173
src/lighting.c
@@ -1,17 +1,10 @@
|
|||||||
/**
|
|
||||||
* @file lighting.c
|
|
||||||
* @brief Advanced lighting system implementation
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 2.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lighting.h"
|
#include "lighting.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void lighting_init(LightingSystem *system)
|
void lighting_init(LightingSystem *system) {
|
||||||
{
|
if (system == NULL)
|
||||||
if (system == NULL) return;
|
return;
|
||||||
|
|
||||||
memset(system, 0, sizeof(LightingSystem));
|
memset(system, 0, sizeof(LightingSystem));
|
||||||
|
|
||||||
@@ -20,162 +13,133 @@ void lighting_init(LightingSystem *system)
|
|||||||
system->camera_position = vec3_create(0.0f, 0.0f, -CAMERA_DISTANCE);
|
system->camera_position = vec3_create(0.0f, 0.0f, -CAMERA_DISTANCE);
|
||||||
system->light_count = 0;
|
system->light_count = 0;
|
||||||
|
|
||||||
/* Add default key light (main light from upper-right-front) */
|
// Default 3-point light setup for standard rendering
|
||||||
|
// Key light
|
||||||
Light key_light = lighting_create_directional(
|
Light key_light = lighting_create_directional(
|
||||||
vec3_create(0.5f, 0.8f, 1.0f),
|
vec3_create(0.5f, 0.8f, 1.0f), color_create(1.0f, 0.98f, 0.95f),
|
||||||
color_create(1.0f, 0.98f, 0.95f),
|
DIFFUSE_INTENSITY);
|
||||||
DIFFUSE_INTENSITY
|
|
||||||
);
|
|
||||||
lighting_add_light(system, &key_light);
|
lighting_add_light(system, &key_light);
|
||||||
|
|
||||||
/* Add fill light (softer light from left) */
|
// Fill light
|
||||||
Light fill_light = lighting_create_directional(
|
Light fill_light = lighting_create_directional(
|
||||||
vec3_create(-0.7f, 0.3f, 0.5f),
|
vec3_create(-0.7f, 0.3f, 0.5f), color_create(0.6f, 0.7f, 1.0f), 0.3f);
|
||||||
color_create(0.6f, 0.7f, 1.0f),
|
|
||||||
0.3f
|
|
||||||
);
|
|
||||||
lighting_add_light(system, &fill_light);
|
lighting_add_light(system, &fill_light);
|
||||||
|
|
||||||
/* Add rim/back light for edge definition */
|
// Rim light to edge definition
|
||||||
Light rim_light = lighting_create_directional(
|
Light rim_light = lighting_create_directional(
|
||||||
vec3_create(0.0f, -0.5f, -1.0f),
|
vec3_create(0.0f, -0.5f, -1.0f), color_create(1.0f, 0.9f, 0.8f), 0.2f);
|
||||||
color_create(1.0f, 0.9f, 0.8f),
|
|
||||||
0.2f
|
|
||||||
);
|
|
||||||
lighting_add_light(system, &rim_light);
|
lighting_add_light(system, &rim_light);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lighting_add_light(LightingSystem *system, const Light *light)
|
int lighting_add_light(LightingSystem *system, const Light *light) {
|
||||||
{
|
if (system == NULL || light == NULL)
|
||||||
if (system == NULL || light == NULL) return -1;
|
return -1;
|
||||||
if (system->light_count >= MAX_LIGHTS) return -1;
|
if (system->light_count >= MAX_LIGHTS)
|
||||||
|
return -1;
|
||||||
system->lights[system->light_count] = *light;
|
system->lights[system->light_count] = *light;
|
||||||
return system->light_count++;
|
return system->light_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Light lighting_create_directional(Vec3 direction, Color color, float intensity)
|
Light lighting_create_directional(Vec3 direction, Color color,
|
||||||
{
|
float intensity) {
|
||||||
Light light;
|
Light light;
|
||||||
memset(&light, 0, sizeof(Light));
|
memset(&light, 0, sizeof(Light));
|
||||||
|
|
||||||
light.type = LIGHT_DIRECTIONAL;
|
light.type = LIGHT_DIRECTIONAL;
|
||||||
light.direction = vec3_normalize(direction);
|
light.direction = vec3_normalize(direction);
|
||||||
light.color = color;
|
light.color = color;
|
||||||
light.intensity = intensity;
|
light.intensity = intensity;
|
||||||
light.enabled = true;
|
light.enabled = true;
|
||||||
|
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
Light lighting_create_point(Vec3 position, Color color, float intensity, float falloff)
|
Light lighting_create_point(Vec3 position, Color color, float intensity,
|
||||||
{
|
float falloff) {
|
||||||
Light light;
|
Light light;
|
||||||
memset(&light, 0, sizeof(Light));
|
memset(&light, 0, sizeof(Light));
|
||||||
|
|
||||||
light.type = LIGHT_POINT;
|
light.type = LIGHT_POINT;
|
||||||
light.position = position;
|
light.position = position;
|
||||||
light.color = color;
|
light.color = color;
|
||||||
light.intensity = intensity;
|
light.intensity = intensity;
|
||||||
light.falloff = falloff;
|
light.falloff = falloff;
|
||||||
light.enabled = true;
|
light.enabled = true;
|
||||||
|
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static float calculate_diffuse(Vec3 light_dir, Vec3 normal) {
|
||||||
* @brief Calculate diffuse lighting contribution
|
|
||||||
*/
|
|
||||||
static float calculate_diffuse(Vec3 light_dir, Vec3 normal)
|
|
||||||
{
|
|
||||||
float ndotl = vec3_dot(normal, light_dir);
|
float ndotl = vec3_dot(normal, light_dir);
|
||||||
return fmaxf(0.0f, ndotl);
|
return fmaxf(0.0f, ndotl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static float calculate_specular(Vec3 light_dir, Vec3 normal, Vec3 view_dir,
|
||||||
* @brief Calculate specular lighting contribution (Blinn-Phong)
|
float shininess) {
|
||||||
*/
|
// Blinn-Phong half vector
|
||||||
static float calculate_specular(Vec3 light_dir, Vec3 normal, Vec3 view_dir, float shininess)
|
|
||||||
{
|
|
||||||
/* Blinn-Phong half-vector */
|
|
||||||
Vec3 half_vec = vec3_normalize(vec3_add(light_dir, view_dir));
|
Vec3 half_vec = vec3_normalize(vec3_add(light_dir, view_dir));
|
||||||
float ndoth = vec3_dot(normal, half_vec);
|
float ndoth = vec3_dot(normal, half_vec);
|
||||||
|
if (ndoth <= 0.0f)
|
||||||
if (ndoth <= 0.0f) return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
return powf(ndoth, shininess);
|
return powf(ndoth, shininess);
|
||||||
}
|
}
|
||||||
|
|
||||||
float lighting_calculate(const LightingSystem *system, Vec3 point,
|
float lighting_calculate(const LightingSystem *system, Vec3 point, Vec3 normal,
|
||||||
Vec3 normal, const Material *material)
|
const Material *material) {
|
||||||
{
|
if (system == NULL || material == NULL)
|
||||||
if (system == NULL || material == NULL) return 0.5f;
|
return 0.5f;
|
||||||
|
|
||||||
/* Start with ambient */
|
|
||||||
float total = system->ambient_intensity;
|
float total = system->ambient_intensity;
|
||||||
|
|
||||||
/* View direction (from point to camera) */
|
|
||||||
Vec3 view_dir = vec3_normalize(vec3_sub(system->camera_position, point));
|
Vec3 view_dir = vec3_normalize(vec3_sub(system->camera_position, point));
|
||||||
|
|
||||||
/* Accumulate contribution from each light */
|
// Accumulate each light source against surface topology
|
||||||
for (int i = 0; i < system->light_count; i++) {
|
for (int i = 0; i < system->light_count; i++) {
|
||||||
const Light *light = &system->lights[i];
|
const Light *light = &system->lights[i];
|
||||||
if (!light->enabled) continue;
|
if (!light->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
Vec3 light_dir;
|
Vec3 light_dir;
|
||||||
float attenuation = 1.0f;
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
if (light->type == LIGHT_DIRECTIONAL) {
|
if (light->type == LIGHT_DIRECTIONAL) {
|
||||||
/* Directional light - direction is constant */
|
|
||||||
light_dir = light->direction;
|
light_dir = light->direction;
|
||||||
} else if (light->type == LIGHT_POINT) {
|
} else if (light->type == LIGHT_POINT) {
|
||||||
/* Point light - calculate direction and attenuation */
|
|
||||||
Vec3 to_light = vec3_sub(light->position, point);
|
Vec3 to_light = vec3_sub(light->position, point);
|
||||||
float dist = vec3_length(to_light);
|
float dist = vec3_length(to_light);
|
||||||
light_dir = vec3_scale(to_light, 1.0f / dist);
|
light_dir = vec3_scale(to_light, 1.0f / dist);
|
||||||
|
// Inverse-square law derived falloff
|
||||||
attenuation = 1.0f / (1.0f + light->falloff * dist * dist);
|
attenuation = 1.0f / (1.0f + light->falloff * dist * dist);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Diffuse */
|
|
||||||
float diffuse = calculate_diffuse(light_dir, normal);
|
float diffuse = calculate_diffuse(light_dir, normal);
|
||||||
|
float specular =
|
||||||
|
calculate_specular(light_dir, normal, view_dir, material->shininess);
|
||||||
|
|
||||||
/* Specular */
|
float contribution =
|
||||||
float specular = calculate_specular(light_dir, normal, view_dir, material->shininess);
|
(diffuse * DIFFUSE_INTENSITY + specular * SPECULAR_INTENSITY) *
|
||||||
|
|
||||||
/* Combine */
|
|
||||||
float contribution = (diffuse * DIFFUSE_INTENSITY +
|
|
||||||
specular * SPECULAR_INTENSITY) *
|
|
||||||
light->intensity * attenuation;
|
light->intensity * attenuation;
|
||||||
|
|
||||||
total += contribution;
|
total += contribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clamp to reasonable range */
|
// We heavily clamp the signal because ASCII has hard boundaries
|
||||||
return fminf(1.0f, fmaxf(0.0f, total));
|
return fminf(1.0f, fmaxf(0.0f, total));
|
||||||
}
|
}
|
||||||
|
|
||||||
Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
||||||
Vec3 normal, const Material *material)
|
Vec3 normal, const Material *material) {
|
||||||
{
|
|
||||||
if (system == NULL || material == NULL) {
|
if (system == NULL || material == NULL) {
|
||||||
return color_create(0.5f, 0.5f, 0.5f);
|
return color_create(0.5f, 0.5f, 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start with ambient */
|
Color result =
|
||||||
Color result = color_scale(
|
color_scale(color_multiply(system->ambient_color, material->ambient),
|
||||||
color_multiply(system->ambient_color, material->ambient),
|
system->ambient_intensity);
|
||||||
system->ambient_intensity
|
|
||||||
);
|
|
||||||
|
|
||||||
/* View direction */
|
|
||||||
Vec3 view_dir = vec3_normalize(vec3_sub(system->camera_position, point));
|
Vec3 view_dir = vec3_normalize(vec3_sub(system->camera_position, point));
|
||||||
|
|
||||||
/* Accumulate from each light */
|
|
||||||
for (int i = 0; i < system->light_count; i++) {
|
for (int i = 0; i < system->light_count; i++) {
|
||||||
const Light *light = &system->lights[i];
|
const Light *light = &system->lights[i];
|
||||||
if (!light->enabled) continue;
|
if (!light->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
Vec3 light_dir;
|
Vec3 light_dir;
|
||||||
float attenuation = 1.0f;
|
float attenuation = 1.0f;
|
||||||
@@ -191,19 +155,15 @@ Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Diffuse */
|
|
||||||
float diff = calculate_diffuse(light_dir, normal);
|
float diff = calculate_diffuse(light_dir, normal);
|
||||||
Color diffuse = color_scale(
|
Color diffuse = color_scale(color_multiply(light->color, material->diffuse),
|
||||||
color_multiply(light->color, material->diffuse),
|
diff * light->intensity * attenuation);
|
||||||
diff * light->intensity * attenuation
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Specular */
|
float spec =
|
||||||
float spec = calculate_specular(light_dir, normal, view_dir, material->shininess);
|
calculate_specular(light_dir, normal, view_dir, material->shininess);
|
||||||
Color specular = color_scale(
|
Color specular =
|
||||||
color_multiply(light->color, material->specular),
|
color_scale(color_multiply(light->color, material->specular),
|
||||||
spec * SPECULAR_INTENSITY * light->intensity * attenuation
|
spec * SPECULAR_INTENSITY * light->intensity * attenuation);
|
||||||
);
|
|
||||||
|
|
||||||
result = color_add(result, color_add(diffuse, specular));
|
result = color_add(result, color_add(diffuse, specular));
|
||||||
}
|
}
|
||||||
@@ -211,8 +171,7 @@ Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
|||||||
return color_clamp(result);
|
return color_clamp(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Material lighting_default_material(void)
|
Material lighting_default_material(void) {
|
||||||
{
|
|
||||||
Material mat;
|
Material mat;
|
||||||
mat.ambient = color_create(0.2f, 0.2f, 0.2f);
|
mat.ambient = color_create(0.2f, 0.2f, 0.2f);
|
||||||
mat.diffuse = color_create(0.8f, 0.8f, 0.8f);
|
mat.diffuse = color_create(0.8f, 0.8f, 0.8f);
|
||||||
@@ -222,38 +181,30 @@ Material lighting_default_material(void)
|
|||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color_create(float r, float g, float b)
|
Color color_create(float r, float g, float b) {
|
||||||
{
|
|
||||||
Color c = {r, g, b};
|
Color c = {r, g, b};
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color_scale(Color c, float s)
|
Color color_scale(Color c, float s) {
|
||||||
{
|
|
||||||
return color_create(c.r * s, c.g * s, c.b * s);
|
return color_create(c.r * s, c.g * s, c.b * s);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color_add(Color a, Color b)
|
Color color_add(Color a, Color b) {
|
||||||
{
|
|
||||||
return color_create(a.r + b.r, a.g + b.g, a.b + b.b);
|
return color_create(a.r + b.r, a.g + b.g, a.b + b.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color_multiply(Color a, Color b)
|
Color color_multiply(Color a, Color b) {
|
||||||
{
|
|
||||||
return color_create(a.r * b.r, a.g * b.g, a.b * b.b);
|
return color_create(a.r * b.r, a.g * b.g, a.b * b.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color_clamp(Color c)
|
Color color_clamp(Color c) {
|
||||||
{
|
return color_create(fminf(1.0f, fmaxf(0.0f, c.r)),
|
||||||
return color_create(
|
|
||||||
fminf(1.0f, fmaxf(0.0f, c.r)),
|
|
||||||
fminf(1.0f, fmaxf(0.0f, c.g)),
|
fminf(1.0f, fmaxf(0.0f, c.g)),
|
||||||
fminf(1.0f, fmaxf(0.0f, c.b))
|
fminf(1.0f, fmaxf(0.0f, c.b)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float color_to_intensity(Color c)
|
float color_to_intensity(Color c) {
|
||||||
{
|
// Rec. 601 Luma mapping coefficients
|
||||||
/* Luminance formula (perceptual weights) */
|
|
||||||
return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
|
return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
|
||||||
}
|
}
|
||||||
|
|||||||
234
src/main.c
234
src/main.c
@@ -1,170 +1,120 @@
|
|||||||
/**
|
|
||||||
* @file main.c
|
|
||||||
* @brief ASCII 3D Renderer - Main entry point
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 2.0.0
|
|
||||||
*
|
|
||||||
* Advanced ASCII 3D text renderer with:
|
|
||||||
* - Phong lighting with multiple light sources
|
|
||||||
* - Multiple render modes (solid, wireframe, shaded)
|
|
||||||
* - ANSI color support (16, 256, truecolor)
|
|
||||||
* - Anti-aliasing
|
|
||||||
* - Multiple shading palettes
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
|
#include "tui.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
/* Global state */
|
// Global shutdown flag for sig handlers
|
||||||
static volatile sig_atomic_t g_running = 1;
|
static volatile sig_atomic_t g_running = 1;
|
||||||
static RenderSettings g_settings;
|
static RenderSettings g_settings;
|
||||||
|
|
||||||
/**
|
static void signal_handler(int sig) {
|
||||||
* @brief Signal handler for graceful shutdown
|
|
||||||
*/
|
|
||||||
static void signal_handler(int sig)
|
|
||||||
{
|
|
||||||
(void)sig;
|
(void)sig;
|
||||||
g_running = 0;
|
g_running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void setup_signals(void) {
|
||||||
* @brief Setup signal handlers
|
|
||||||
*/
|
|
||||||
static void setup_signals(void)
|
|
||||||
{
|
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
sa.sa_handler = signal_handler;
|
sa.sa_handler = signal_handler;
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
|
|
||||||
|
// We catch these to orchestrate a clean renderer shutdown
|
||||||
|
// Without this, the terminal will stay trapped in alternate mode
|
||||||
sigaction(SIGINT, &sa, NULL);
|
sigaction(SIGINT, &sa, NULL);
|
||||||
sigaction(SIGTERM, &sa, NULL);
|
sigaction(SIGTERM, &sa, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void print_usage(const char *program_name) {
|
||||||
* @brief Print usage information
|
|
||||||
*/
|
|
||||||
static void print_usage(const char *program_name)
|
|
||||||
{
|
|
||||||
printf("\n");
|
|
||||||
printf(" ╔═══════════════════════════════════════════════════════════╗\n");
|
|
||||||
printf(" ║ ASCII 3D RENDERER v2.0.0 ║\n");
|
|
||||||
printf(" ║ Advanced 3D Text Rendering with Phong Lighting ║\n");
|
|
||||||
printf(" ╚═══════════════════════════════════════════════════════════╝\n");
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
printf(" ASCII 3D RENDERER\n");
|
||||||
|
printf(" Advanced 3D Text Rendering with Phong Lighting & TUI\n\n");
|
||||||
printf("Usage: %s [OPTIONS] [TEXT]\n\n", program_name);
|
printf("Usage: %s [OPTIONS] [TEXT]\n\n", program_name);
|
||||||
|
|
||||||
printf("ROTATION OPTIONS:\n");
|
printf("ROTATION OPTIONS:\n");
|
||||||
printf(" -s <speed> Rotation speed multiplier (default: 1.0)\n");
|
printf(" -s <speed> Playback speed multiplier (default: 1.0)\n");
|
||||||
printf(" -x Enable X-axis rotation\n");
|
printf(" -x Enable X-axis rotation\n");
|
||||||
printf(" -y Enable Y-axis rotation (default)\n");
|
printf(" -y Enable Y-axis rotation (default)\n");
|
||||||
printf(" -z Enable Z-axis rotation\n");
|
printf(" -z Enable Z-axis rotation\n");
|
||||||
printf(" -a Enable all axis rotations\n");
|
printf(" -a Enable all rotational axes\n\n");
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
printf("RENDER MODE OPTIONS:\n");
|
printf("RENDER MODE OPTIONS:\n");
|
||||||
printf(" -m <mode> Render mode:\n");
|
printf(" -m <mode> Mode override:\n");
|
||||||
printf(" 0 = Solid (filled)\n");
|
printf(" 0 = Solid, 1 = Wireframe\n");
|
||||||
printf(" 1 = Wireframe (edges only)\n");
|
printf(" 2 = Points, 3 = Shaded [default]\n\n");
|
||||||
printf(" 2 = Points (sparse)\n");
|
|
||||||
printf(" 3 = Shaded (full Phong lighting) [default]\n");
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
printf("COLOR OPTIONS:\n");
|
printf("COLOR OPTIONS:\n");
|
||||||
printf(" -c <mode> Color mode:\n");
|
printf(" -c <mode> Color strategy:\n");
|
||||||
printf(" 0 = Monochrome ASCII [default]\n");
|
printf(" 0 = Mono [default], 1 = ANSI 16\n");
|
||||||
printf(" 1 = 16-color ANSI\n");
|
printf(" 2 = ANSI 256, 3 = Truecolor RGB\n\n");
|
||||||
printf(" 2 = 256-color ANSI\n");
|
|
||||||
printf(" 3 = Truecolor (24-bit RGB)\n");
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
printf("QUALITY OPTIONS:\n");
|
printf("QUALITY OPTIONS:\n");
|
||||||
printf(" -q <quality> Render quality (0.5 - 2.0, default: 1.0)\n");
|
printf(" -q <quality> Oversampling quality (0.5 - 2.0, default: 1.0)\n");
|
||||||
printf(" -A Enable anti-aliasing\n");
|
printf(" -A Force Anti-aliasing\n");
|
||||||
printf(" -p <palette> Shading palette:\n");
|
printf(" -p <palette> Ascii palette tier (0-3, default: 1)\n\n");
|
||||||
printf(" 0 = Standard (10 levels)\n");
|
|
||||||
printf(" 1 = Extended (70 levels) [default]\n");
|
|
||||||
printf(" 2 = Block characters\n");
|
|
||||||
printf(" 3 = Minimal (6 levels)\n");
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
printf("OTHER OPTIONS:\n");
|
printf("OTHER OPTIONS:\n");
|
||||||
printf(" -f Show FPS counter\n");
|
printf(" -f Display real-time FPS overlay\n");
|
||||||
printf(" -h Show this help message\n");
|
printf(" -h Show this help dialog\n\n");
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
printf("EXAMPLES:\n");
|
printf("TUI CONTROLS (active during render):\n");
|
||||||
printf(" %s HELLO Simple Y-axis rotation\n", program_name);
|
printf(" [Typing] Changes dynamically rendered text\n");
|
||||||
printf(" %s -a -c3 WORLD Truecolor tumbling\n", program_name);
|
printf(" [Space] Pause/Resume rotation\n");
|
||||||
printf(" %s -m1 -s2 WIRE Fast wireframe\n", program_name);
|
printf(" [w/s] Modify rotation speed\n");
|
||||||
printf(" %s -A -q1.5 -c2 HQ High quality with AA\n", program_name);
|
printf(" [m] Cycle render modes\n");
|
||||||
printf(" %s -p2 BLOCKS Block character style\n", program_name);
|
printf(" [c] Cycle color pipelines\n");
|
||||||
printf("\n");
|
printf(" [p] Cycle ASCII density palettes\n");
|
||||||
|
printf(" [q / ESC] Terminate gracefully\n\n");
|
||||||
printf("INTERACTIVE KEYS (during rendering):\n");
|
|
||||||
printf(" Ctrl+C Exit\n");
|
|
||||||
printf("\n");
|
|
||||||
printf("Supported characters: A-Z, a-z, 0-9\n");
|
|
||||||
printf("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static bool parse_cli_args(int argc, char *argv[], RotationState *rotation,
|
||||||
* @brief Parse command line arguments
|
char *text_buffer, size_t buf_len) {
|
||||||
*/
|
|
||||||
static bool parse_arguments(int argc, char *argv[],
|
|
||||||
RotationState *rotation, const char **text)
|
|
||||||
{
|
|
||||||
*rotation = renderer_default_rotation();
|
*rotation = renderer_default_rotation();
|
||||||
g_settings = renderer_default_settings();
|
g_settings = renderer_default_settings();
|
||||||
*text = "3D";
|
|
||||||
|
// Set a default demo text if none is supplied
|
||||||
|
strncpy(text_buffer, "3D", buf_len - 1);
|
||||||
|
text_buffer[buf_len - 1] = '\0';
|
||||||
|
|
||||||
bool explicit_rotation = false;
|
bool explicit_rotation = false;
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (argv[i][0] == '-') {
|
if (argv[i][0] == '-') {
|
||||||
const char *opt = &argv[i][1];
|
const char *opt = &argv[i][1];
|
||||||
|
|
||||||
while (*opt) {
|
while (*opt) {
|
||||||
switch (*opt) {
|
switch (*opt) {
|
||||||
case 's':
|
case 's':
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
rotation->speed = (float)atof(argv[++i]);
|
rotation->speed = (float)atof(argv[++i]);
|
||||||
if (rotation->speed <= 0.0f) rotation->speed = 1.0f;
|
if (rotation->speed <= 0.0f)
|
||||||
|
rotation->speed = 1.0f;
|
||||||
}
|
}
|
||||||
goto next_arg;
|
goto next_arg;
|
||||||
|
|
||||||
case 'x':
|
case 'x':
|
||||||
rotation->enable_x = true;
|
rotation->enable_x = true;
|
||||||
explicit_rotation = true;
|
explicit_rotation = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'y':
|
case 'y':
|
||||||
rotation->enable_y = true;
|
rotation->enable_y = true;
|
||||||
explicit_rotation = true;
|
explicit_rotation = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'z':
|
case 'z':
|
||||||
rotation->enable_z = true;
|
rotation->enable_z = true;
|
||||||
explicit_rotation = true;
|
explicit_rotation = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'a':
|
case 'a':
|
||||||
rotation->enable_x = true;
|
rotation->enable_x = true;
|
||||||
rotation->enable_y = true;
|
rotation->enable_y = true;
|
||||||
rotation->enable_z = true;
|
rotation->enable_z = true;
|
||||||
explicit_rotation = true;
|
explicit_rotation = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
int mode = atoi(argv[++i]);
|
int mode = atoi(argv[++i]);
|
||||||
@@ -173,7 +123,6 @@ static bool parse_arguments(int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
goto next_arg;
|
goto next_arg;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
int cmode = atoi(argv[++i]);
|
int cmode = atoi(argv[++i]);
|
||||||
@@ -182,19 +131,18 @@ static bool parse_arguments(int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
goto next_arg;
|
goto next_arg;
|
||||||
|
|
||||||
case 'q':
|
case 'q':
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
g_settings.quality = (float)atof(argv[++i]);
|
g_settings.quality = (float)atof(argv[++i]);
|
||||||
if (g_settings.quality < 0.5f) g_settings.quality = 0.5f;
|
if (g_settings.quality < 0.5f)
|
||||||
if (g_settings.quality > 2.0f) g_settings.quality = 2.0f;
|
g_settings.quality = 0.5f;
|
||||||
|
if (g_settings.quality > 2.0f)
|
||||||
|
g_settings.quality = 2.0f;
|
||||||
}
|
}
|
||||||
goto next_arg;
|
goto next_arg;
|
||||||
|
|
||||||
case 'A':
|
case 'A':
|
||||||
g_settings.anti_aliasing = true;
|
g_settings.anti_aliasing = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
int pal = atoi(argv[++i]);
|
int pal = atoi(argv[++i]);
|
||||||
@@ -203,49 +151,49 @@ static bool parse_arguments(int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
goto next_arg;
|
goto next_arg;
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
g_settings.show_fps = true;
|
g_settings.show_fps = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Unknown option: -%c\n", *opt);
|
fprintf(stderr, "Unrecognized param: -%c\n", *opt);
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
opt++;
|
opt++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*text = argv[i];
|
strncpy(text_buffer, argv[i], buf_len - 1);
|
||||||
|
text_buffer[buf_len - 1] = '\0';
|
||||||
}
|
}
|
||||||
next_arg:;
|
next_arg:;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle rotation defaults */
|
// Inject Y-axis animation as a baseline if the user provided no explicit axis
|
||||||
if (explicit_rotation && !rotation->enable_y) {
|
// constraints
|
||||||
/* User explicitly chose axes */
|
if (!explicit_rotation) {
|
||||||
} else if (!explicit_rotation) {
|
|
||||||
rotation->enable_y = true;
|
rotation->enable_y = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void tui_help_overlay(void) {
|
||||||
* @brief Main render loop with advanced features
|
printf("\033[%d;1H\033[90m [SPACE] Pause | [w/s] Speed | [c] Color | [m] "
|
||||||
*/
|
"Mode | [p] Palette | [q/ESC] Quit | Type to edit \033[0m",
|
||||||
static void render_loop(const char *text, RotationState *rotation)
|
SCREEN_HEIGHT);
|
||||||
{
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void core_render_loop(char *text_input, RotationState *rotation) {
|
||||||
double last_time = timing_get_seconds();
|
double last_time = timing_get_seconds();
|
||||||
double fps_update_time = last_time;
|
double fps_update_time = last_time;
|
||||||
int frame_count = 0;
|
int frame_count = 0;
|
||||||
double current_fps = 0.0;
|
double current_fps = 0.0;
|
||||||
|
|
||||||
/* Apply settings */
|
// Mount the requested settings into our rendering context
|
||||||
renderer_set_mode(g_settings.mode);
|
renderer_set_mode(g_settings.mode);
|
||||||
renderer_set_color_mode(g_settings.color_mode);
|
renderer_set_color_mode(g_settings.color_mode);
|
||||||
renderer_set_palette(g_settings.palette_index);
|
renderer_set_palette(g_settings.palette_index);
|
||||||
@@ -255,7 +203,17 @@ static void render_loop(const char *text, RotationState *rotation)
|
|||||||
double delta_time = current_time - last_time;
|
double delta_time = current_time - last_time;
|
||||||
last_time = current_time;
|
last_time = current_time;
|
||||||
|
|
||||||
/* Update FPS counter */
|
// Let's poll non-blocking CLI input to keep interactivity responsive
|
||||||
|
if (tui_process_input(rotation, &g_settings, text_input, 64)) {
|
||||||
|
g_running = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must sync local settings back to the engine incase TUI altered them
|
||||||
|
renderer_set_mode(g_settings.mode);
|
||||||
|
renderer_set_color_mode(g_settings.color_mode);
|
||||||
|
renderer_set_palette(g_settings.palette_index);
|
||||||
|
|
||||||
frame_count++;
|
frame_count++;
|
||||||
if (current_time - fps_update_time >= 1.0) {
|
if (current_time - fps_update_time >= 1.0) {
|
||||||
current_fps = (double)frame_count / (current_time - fps_update_time);
|
current_fps = (double)frame_count / (current_time - fps_update_time);
|
||||||
@@ -263,68 +221,70 @@ static void render_loop(const char *text, RotationState *rotation)
|
|||||||
fps_update_time = current_time;
|
fps_update_time = current_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update rotation */
|
if (g_settings.auto_rotate) {
|
||||||
renderer_update_rotation(rotation, delta_time);
|
renderer_update_rotation(rotation, delta_time);
|
||||||
|
}
|
||||||
|
|
||||||
/* Render frame */
|
|
||||||
renderer_clear();
|
renderer_clear();
|
||||||
renderer_draw_text_ex(text, rotation, &g_settings);
|
renderer_draw_text_ex(text_input, rotation, &g_settings);
|
||||||
|
|
||||||
/* Present with appropriate color mode */
|
|
||||||
if (g_settings.color_mode != COLOR_MODE_MONO) {
|
if (g_settings.color_mode != COLOR_MODE_MONO) {
|
||||||
renderer_present_color(&g_settings);
|
renderer_present_color(&g_settings);
|
||||||
} else {
|
} else {
|
||||||
renderer_present();
|
renderer_present();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show FPS if enabled */
|
|
||||||
if (g_settings.show_fps) {
|
if (g_settings.show_fps) {
|
||||||
printf("\033[1;1H\033[7m FPS: %.1f | Voxels: %d \033[0m",
|
printf("\033[1;1H\033[7m FPS: %.1f | Voxels: %d \033[0m", current_fps,
|
||||||
current_fps, renderer_get_stats().triangles);
|
renderer_get_stats().triangles);
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Limit frame rate */
|
tui_help_overlay();
|
||||||
|
|
||||||
timing_limit_fps(current_time, TARGET_FPS);
|
timing_limit_fps(current_time, TARGET_FPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
int main(int argc, char *argv[]) {
|
||||||
* @brief Program entry point
|
|
||||||
*/
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
RotationState rotation;
|
RotationState rotation;
|
||||||
const char *text;
|
char text_buffer[64] = {0};
|
||||||
|
|
||||||
/* Parse command line */
|
if (!parse_cli_args(argc, argv, &rotation, text_buffer,
|
||||||
if (!parse_arguments(argc, argv, &rotation, &text)) {
|
sizeof(text_buffer))) {
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup signal handlers */
|
|
||||||
setup_signals();
|
setup_signals();
|
||||||
|
|
||||||
/* Initialize renderer */
|
|
||||||
if (renderer_init() != 0) {
|
if (renderer_init() != 0) {
|
||||||
fprintf(stderr, "Failed to initialize renderer\n");
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"Renderer initialization failed due to context limits or IO error.\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enter alternate screen buffer for clean exit */
|
// Elevating the console environment
|
||||||
renderer_enter_alternate_screen();
|
renderer_enter_alternate_screen();
|
||||||
renderer_clear_terminal();
|
renderer_clear_terminal();
|
||||||
renderer_hide_cursor();
|
renderer_hide_cursor();
|
||||||
|
|
||||||
/* Run main loop */
|
if (tui_init() < 0) {
|
||||||
render_loop(text, &rotation);
|
renderer_show_cursor();
|
||||||
|
renderer_exit_alternate_screen();
|
||||||
|
renderer_cleanup();
|
||||||
|
fprintf(stderr, "Failed to instantiate TTY interface. Falling back.\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Cleanup */
|
core_render_loop(text_buffer, &rotation);
|
||||||
|
|
||||||
|
// Tearing down custom modes properly ensures the user doesn't end up with a
|
||||||
|
// broken shell prompt
|
||||||
|
tui_cleanup();
|
||||||
renderer_show_cursor();
|
renderer_show_cursor();
|
||||||
renderer_exit_alternate_screen();
|
renderer_exit_alternate_screen();
|
||||||
renderer_cleanup();
|
renderer_cleanup();
|
||||||
|
|
||||||
printf("Goodbye!\n");
|
printf("Goodbye!\n");
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
449
src/renderer.c
449
src/renderer.c
@@ -1,108 +1,54 @@
|
|||||||
/**
|
|
||||||
* @file renderer.c
|
|
||||||
* @brief Advanced rendering engine implementation
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 2.0.0
|
|
||||||
*
|
|
||||||
* Features:
|
|
||||||
* - Phong lighting model with multiple light sources
|
|
||||||
* - Sub-pixel anti-aliasing
|
|
||||||
* - Multiple render modes (solid, wireframe, points)
|
|
||||||
* - ANSI color support (16, 256, truecolor)
|
|
||||||
* - High-quality ASCII shading with extended palettes
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "vec3.h"
|
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
#include "vec3.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
/*============================================================================
|
// Internal Frame Buffer state
|
||||||
* GLOBAL STATE
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/* Screen buffer - character display */
|
|
||||||
static char g_screen[SCREEN_HEIGHT][SCREEN_WIDTH + 1];
|
static char g_screen[SCREEN_HEIGHT][SCREEN_WIDTH + 1];
|
||||||
|
|
||||||
/* Depth buffer for z-testing */
|
|
||||||
static float g_zbuffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
static float g_zbuffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
/* Normal buffer for post-processing effects */
|
|
||||||
static Vec3 g_normal_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
static Vec3 g_normal_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
/* Color buffer for colored output */
|
|
||||||
static Color g_color_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
static Color g_color_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
/* Intensity accumulator for anti-aliasing */
|
// Multisampling accumulator layers
|
||||||
static float g_intensity_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
static float g_intensity_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
static int g_sample_count[SCREEN_HEIGHT][SCREEN_WIDTH];
|
static int g_sample_count[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
/* Lighting system */
|
|
||||||
static LightingSystem g_lighting;
|
static LightingSystem g_lighting;
|
||||||
|
|
||||||
/* Current render settings */
|
|
||||||
static RenderSettings g_settings;
|
static RenderSettings g_settings;
|
||||||
|
|
||||||
/* Current render mode */
|
|
||||||
static RenderMode g_render_mode = RENDER_MODE_SHADED;
|
static RenderMode g_render_mode = RENDER_MODE_SHADED;
|
||||||
|
|
||||||
/* Current color mode */
|
|
||||||
static ColorMode g_color_mode = COLOR_MODE_MONO;
|
static ColorMode g_color_mode = COLOR_MODE_MONO;
|
||||||
|
|
||||||
/* Frame statistics */
|
|
||||||
static FrameStats g_stats;
|
static FrameStats g_stats;
|
||||||
|
|
||||||
/* Shading palettes */
|
static const char *g_palettes[] = {SHADE_CHARS_STANDARD, SHADE_CHARS_EXTENDED,
|
||||||
static const char *g_palettes[] = {
|
SHADE_CHARS_BLOCK, SHADE_CHARS_MINIMAL};
|
||||||
SHADE_CHARS_STANDARD,
|
static const int g_palette_sizes[] = {SHADE_COUNT_STANDARD,
|
||||||
SHADE_CHARS_EXTENDED,
|
SHADE_COUNT_EXTENDED, SHADE_COUNT_BLOCK,
|
||||||
SHADE_CHARS_BLOCK,
|
SHADE_COUNT_MINIMAL};
|
||||||
SHADE_CHARS_MINIMAL
|
static int g_current_palette = 1;
|
||||||
};
|
|
||||||
static const int g_palette_sizes[] = {
|
|
||||||
SHADE_COUNT_STANDARD,
|
|
||||||
SHADE_COUNT_EXTENDED,
|
|
||||||
SHADE_COUNT_BLOCK,
|
|
||||||
SHADE_COUNT_MINIMAL
|
|
||||||
};
|
|
||||||
static int g_current_palette = 1; /* Extended by default */
|
|
||||||
|
|
||||||
/* Default material */
|
|
||||||
static Material g_material;
|
static Material g_material;
|
||||||
|
|
||||||
/* Voxel counter for stats */
|
|
||||||
static int g_voxel_count;
|
static int g_voxel_count;
|
||||||
|
|
||||||
/*============================================================================
|
// Translates 3D world space points down to 2D screen coordinates, applying FOV
|
||||||
* PROJECTION
|
// projection matrix scaling
|
||||||
*============================================================================*/
|
static bool project_point(Vec3 point, float *out_x, float *out_y,
|
||||||
|
float *out_z) {
|
||||||
/**
|
// Standard terminal font width/height aspect ratio is roughly 2.2 for
|
||||||
* @brief Project a 3D point to 2D screen coordinates with perspective
|
// consistent rendering
|
||||||
*/
|
|
||||||
static bool project_point(Vec3 point, float *out_x, float *out_y, float *out_z)
|
|
||||||
{
|
|
||||||
/* Character aspect ratio compensation */
|
|
||||||
float aspect = (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT / 2.2f;
|
float aspect = (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT / 2.2f;
|
||||||
|
|
||||||
/* Camera space transformation */
|
|
||||||
float z = point.z + CAMERA_DISTANCE;
|
float z = point.z + CAMERA_DISTANCE;
|
||||||
|
|
||||||
/* Near plane clipping */
|
// Discard geometry outside camera frustum planes
|
||||||
if (z < NEAR_PLANE) {
|
if (z < NEAR_PLANE || z > FAR_PLANE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Far plane clipping */
|
|
||||||
if (z > FAR_PLANE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perspective division */
|
|
||||||
float scale = FIELD_OF_VIEW / z;
|
float scale = FIELD_OF_VIEW / z;
|
||||||
*out_x = point.x * scale * aspect + (float)SCREEN_WIDTH / 2.0f;
|
*out_x = point.x * scale * aspect + (float)SCREEN_WIDTH / 2.0f;
|
||||||
*out_y = -point.y * scale + (float)SCREEN_HEIGHT / 2.0f;
|
*out_y = -point.y * scale + (float)SCREEN_HEIGHT / 2.0f;
|
||||||
@@ -111,47 +57,34 @@ static bool project_point(Vec3 point, float *out_x, float *out_y, float *out_z)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*============================================================================
|
static void plot_point_lit(float sx, float sy, float sz, Vec3 point,
|
||||||
* PIXEL PLOTTING
|
Vec3 normal) {
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Plot a point with full lighting calculation
|
|
||||||
*/
|
|
||||||
static void plot_point_lit(float sx, float sy, float sz, Vec3 point, Vec3 normal)
|
|
||||||
{
|
|
||||||
int ix = (int)sx;
|
int ix = (int)sx;
|
||||||
int iy = (int)sy;
|
int iy = (int)sy;
|
||||||
|
|
||||||
/* Bounds check */
|
if (ix < 0 || ix >= SCREEN_WIDTH || iy < 0 || iy >= SCREEN_HEIGHT)
|
||||||
if (ix < 0 || ix >= SCREEN_WIDTH || iy < 0 || iy >= SCREEN_HEIGHT) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Depth test */
|
// Obscurance clipping (standard Z-buffer check)
|
||||||
if (sz >= g_zbuffer[iy][ix]) {
|
if (sz >= g_zbuffer[iy][ix])
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
g_zbuffer[iy][ix] = sz;
|
g_zbuffer[iy][ix] = sz;
|
||||||
g_normal_buffer[iy][ix] = normal;
|
g_normal_buffer[iy][ix] = normal;
|
||||||
g_voxel_count++;
|
g_voxel_count++;
|
||||||
|
|
||||||
/* Calculate lighting */
|
|
||||||
float intensity;
|
float intensity;
|
||||||
Color color;
|
Color color;
|
||||||
|
|
||||||
if (g_render_mode == RENDER_MODE_SHADED) {
|
if (g_render_mode == RENDER_MODE_SHADED) {
|
||||||
/* Full Phong lighting */
|
// Evaluate the full lighting model recursively for shading
|
||||||
color = lighting_calculate_color(&g_lighting, point, normal, &g_material);
|
color = lighting_calculate_color(&g_lighting, point, normal, &g_material);
|
||||||
intensity = color_to_intensity(color);
|
intensity = color_to_intensity(color);
|
||||||
} else if (g_render_mode == RENDER_MODE_SOLID) {
|
} else if (g_render_mode == RENDER_MODE_SOLID) {
|
||||||
/* Simple normal-based shading */
|
|
||||||
intensity = (normal.z + 1.0f) * 0.5f;
|
intensity = (normal.z + 1.0f) * 0.5f;
|
||||||
intensity = fmaxf(0.0f, fminf(1.0f, intensity));
|
intensity = fmaxf(0.0f, fminf(1.0f, intensity));
|
||||||
color = color_scale(g_settings.base_color, intensity);
|
color = color_scale(g_settings.base_color, intensity);
|
||||||
} else {
|
} else {
|
||||||
/* Wireframe/points - full brightness */
|
|
||||||
intensity = 1.0f;
|
intensity = 1.0f;
|
||||||
color = g_settings.base_color;
|
color = g_settings.base_color;
|
||||||
}
|
}
|
||||||
@@ -160,35 +93,31 @@ static void plot_point_lit(float sx, float sy, float sz, Vec3 point, Vec3 normal
|
|||||||
g_intensity_buffer[iy][ix] = intensity;
|
g_intensity_buffer[iy][ix] = intensity;
|
||||||
g_sample_count[iy][ix] = 1;
|
g_sample_count[iy][ix] = 1;
|
||||||
|
|
||||||
/* Convert intensity to ASCII character */
|
|
||||||
const char *palette = g_palettes[g_current_palette];
|
const char *palette = g_palettes[g_current_palette];
|
||||||
int palette_size = g_palette_sizes[g_current_palette];
|
int palette_size = g_palette_sizes[g_current_palette];
|
||||||
|
|
||||||
int shade_idx = (int)(intensity * (float)(palette_size - 1) + 0.5f);
|
int shade_idx = (int)(intensity * (float)(palette_size - 1) + 0.5f);
|
||||||
shade_idx = shade_idx < 0 ? 0 : (shade_idx >= palette_size ? palette_size - 1 : shade_idx);
|
shade_idx = shade_idx < 0
|
||||||
|
? 0
|
||||||
|
: (shade_idx >= palette_size ? palette_size - 1 : shade_idx);
|
||||||
g_screen[iy][ix] = palette[shade_idx];
|
g_screen[iy][ix] = palette[shade_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Sub-pixel accumulation pass for anti-aliasing edges
|
||||||
* @brief Plot with anti-aliasing (accumulate samples)
|
static void plot_point_aa(float sx, float sy, float sz, Vec3 point,
|
||||||
*/
|
Vec3 normal) {
|
||||||
static void plot_point_aa(float sx, float sy, float sz, Vec3 point, Vec3 normal)
|
|
||||||
{
|
|
||||||
/* Sub-pixel offset for AA */
|
|
||||||
int ix = (int)sx;
|
int ix = (int)sx;
|
||||||
int iy = (int)sy;
|
int iy = (int)sy;
|
||||||
|
|
||||||
if (ix < 0 || ix >= SCREEN_WIDTH || iy < 0 || iy >= SCREEN_HEIGHT) {
|
if (ix < 0 || ix >= SCREEN_WIDTH || iy < 0 || iy >= SCREEN_HEIGHT)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* For AA, we accumulate and average */
|
|
||||||
if (sz < g_zbuffer[iy][ix]) {
|
if (sz < g_zbuffer[iy][ix]) {
|
||||||
g_zbuffer[iy][ix] = sz;
|
g_zbuffer[iy][ix] = sz;
|
||||||
g_normal_buffer[iy][ix] = normal;
|
g_normal_buffer[iy][ix] = normal;
|
||||||
|
|
||||||
Color color = lighting_calculate_color(&g_lighting, point, normal, &g_material);
|
Color color =
|
||||||
|
lighting_calculate_color(&g_lighting, point, normal, &g_material);
|
||||||
float intensity = color_to_intensity(color);
|
float intensity = color_to_intensity(color);
|
||||||
|
|
||||||
g_intensity_buffer[iy][ix] += intensity;
|
g_intensity_buffer[iy][ix] += intensity;
|
||||||
@@ -198,104 +127,70 @@ static void plot_point_aa(float sx, float sy, float sz, Vec3 point, Vec3 normal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*============================================================================
|
static bool neighbor_empty(const unsigned char *glyph, int x, int y, int dx,
|
||||||
* EDGE DETECTION FOR SURFACE RENDERING
|
int dy) {
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
static bool neighbor_empty(const unsigned char *glyph, int x, int y, int dx, int dy)
|
|
||||||
{
|
|
||||||
int nx = x + dx;
|
int nx = x + dx;
|
||||||
int ny = y + dy;
|
int ny = y + dy;
|
||||||
|
if (nx < 0 || nx >= FONT_WIDTH || ny < 0 || ny >= FONT_HEIGHT)
|
||||||
if (nx < 0 || nx >= FONT_WIDTH || ny < 0 || ny >= FONT_HEIGHT) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return !font_pixel_set(glyph, nx, ny);
|
return !font_pixel_set(glyph, nx, ny);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Derives pseudo-SMOOTH normals for voxel shapes based on neighboring vacancies
|
||||||
* @brief Calculate smooth normal by averaging adjacent face normals
|
|
||||||
*/
|
|
||||||
static Vec3 calculate_smooth_normal(const unsigned char *glyph, int gx, int gy,
|
static Vec3 calculate_smooth_normal(const unsigned char *glyph, int gx, int gy,
|
||||||
float gz, bool is_front, bool is_back)
|
float gz, bool is_front, bool is_back) {
|
||||||
{
|
|
||||||
Vec3 normal = vec3_create(0.0f, 0.0f, 0.0f);
|
Vec3 normal = vec3_create(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
/* Front/back face contribution */
|
if (is_front)
|
||||||
if (is_front) {
|
|
||||||
normal = vec3_add(normal, vec3_create(0.0f, 0.0f, 1.0f));
|
normal = vec3_add(normal, vec3_create(0.0f, 0.0f, 1.0f));
|
||||||
}
|
if (is_back)
|
||||||
if (is_back) {
|
|
||||||
normal = vec3_add(normal, vec3_create(0.0f, 0.0f, -1.0f));
|
normal = vec3_add(normal, vec3_create(0.0f, 0.0f, -1.0f));
|
||||||
}
|
|
||||||
|
|
||||||
/* Side face contributions */
|
if (neighbor_empty(glyph, gx, gy, -1, 0))
|
||||||
if (neighbor_empty(glyph, gx, gy, -1, 0)) {
|
|
||||||
normal = vec3_add(normal, vec3_create(-1.0f, 0.0f, 0.0f));
|
normal = vec3_add(normal, vec3_create(-1.0f, 0.0f, 0.0f));
|
||||||
}
|
if (neighbor_empty(glyph, gx, gy, 1, 0))
|
||||||
if (neighbor_empty(glyph, gx, gy, 1, 0)) {
|
|
||||||
normal = vec3_add(normal, vec3_create(1.0f, 0.0f, 0.0f));
|
normal = vec3_add(normal, vec3_create(1.0f, 0.0f, 0.0f));
|
||||||
}
|
if (neighbor_empty(glyph, gx, gy, 0, -1))
|
||||||
if (neighbor_empty(glyph, gx, gy, 0, -1)) {
|
|
||||||
normal = vec3_add(normal, vec3_create(0.0f, 1.0f, 0.0f));
|
normal = vec3_add(normal, vec3_create(0.0f, 1.0f, 0.0f));
|
||||||
}
|
if (neighbor_empty(glyph, gx, gy, 0, 1))
|
||||||
if (neighbor_empty(glyph, gx, gy, 0, 1)) {
|
|
||||||
normal = vec3_add(normal, vec3_create(0.0f, -1.0f, 0.0f));
|
normal = vec3_add(normal, vec3_create(0.0f, -1.0f, 0.0f));
|
||||||
}
|
|
||||||
|
|
||||||
/* Diagonal contributions for smoother edges */
|
if (neighbor_empty(glyph, gx, gy, -1, -1))
|
||||||
if (neighbor_empty(glyph, gx, gy, -1, -1)) {
|
normal =
|
||||||
normal = vec3_add(normal, vec3_scale(vec3_create(-0.707f, 0.707f, 0.0f), 0.5f));
|
vec3_add(normal, vec3_scale(vec3_create(-0.707f, 0.707f, 0.0f), 0.5f));
|
||||||
}
|
if (neighbor_empty(glyph, gx, gy, 1, -1))
|
||||||
if (neighbor_empty(glyph, gx, gy, 1, -1)) {
|
normal =
|
||||||
normal = vec3_add(normal, vec3_scale(vec3_create(0.707f, 0.707f, 0.0f), 0.5f));
|
vec3_add(normal, vec3_scale(vec3_create(0.707f, 0.707f, 0.0f), 0.5f));
|
||||||
}
|
if (neighbor_empty(glyph, gx, gy, -1, 1))
|
||||||
if (neighbor_empty(glyph, gx, gy, -1, 1)) {
|
normal =
|
||||||
normal = vec3_add(normal, vec3_scale(vec3_create(-0.707f, -0.707f, 0.0f), 0.5f));
|
vec3_add(normal, vec3_scale(vec3_create(-0.707f, -0.707f, 0.0f), 0.5f));
|
||||||
}
|
if (neighbor_empty(glyph, gx, gy, 1, 1))
|
||||||
if (neighbor_empty(glyph, gx, gy, 1, 1)) {
|
normal =
|
||||||
normal = vec3_add(normal, vec3_scale(vec3_create(0.707f, -0.707f, 0.0f), 0.5f));
|
vec3_add(normal, vec3_scale(vec3_create(0.707f, -0.707f, 0.0f), 0.5f));
|
||||||
}
|
|
||||||
|
|
||||||
(void)gz; /* Suppress unused warning */
|
(void)gz;
|
||||||
|
|
||||||
return vec3_normalize(normal);
|
return vec3_normalize(normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*============================================================================
|
static void render_char_advanced(char c, float offset_x,
|
||||||
* CHARACTER RENDERING
|
const RotationState *rotation,
|
||||||
*============================================================================*/
|
const RenderSettings *settings) {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Render a single 3D character with advanced lighting
|
|
||||||
*/
|
|
||||||
static void render_char_advanced(char c, float offset_x, const RotationState *rotation,
|
|
||||||
const RenderSettings *settings)
|
|
||||||
{
|
|
||||||
const unsigned char *glyph = font_get_glyph(c);
|
const unsigned char *glyph = font_get_glyph(c);
|
||||||
if (glyph == NULL) {
|
if (!glyph)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Center offsets */
|
|
||||||
float cx = (float)FONT_WIDTH / 2.0f;
|
float cx = (float)FONT_WIDTH / 2.0f;
|
||||||
float cy = (float)FONT_HEIGHT / 2.0f;
|
float cy = (float)FONT_HEIGHT / 2.0f;
|
||||||
float cz = EXTRUSION_DEPTH / 2.0f;
|
float cz = EXTRUSION_DEPTH / 2.0f;
|
||||||
|
|
||||||
/* Quality-adjusted voxel step */
|
|
||||||
float step = VOXEL_STEP / settings->quality;
|
float step = VOXEL_STEP / settings->quality;
|
||||||
|
|
||||||
/* Iterate through each voxel */
|
|
||||||
for (int gy = 0; gy < FONT_HEIGHT; gy++) {
|
for (int gy = 0; gy < FONT_HEIGHT; gy++) {
|
||||||
for (int gx = 0; gx < FONT_WIDTH; gx++) {
|
for (int gx = 0; gx < FONT_WIDTH; gx++) {
|
||||||
if (!font_pixel_set(glyph, gx, gy)) {
|
if (!font_pixel_set(glyph, gx, gy))
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
/* Render along Z depth (extrusion) */
|
|
||||||
for (float gz = 0.0f; gz <= EXTRUSION_DEPTH; gz += step) {
|
for (float gz = 0.0f; gz <= EXTRUSION_DEPTH; gz += step) {
|
||||||
/* Determine if this is a surface voxel */
|
|
||||||
bool is_front = (gz < step);
|
bool is_front = (gz < step);
|
||||||
bool is_back = (gz > EXTRUSION_DEPTH - step);
|
bool is_back = (gz > EXTRUSION_DEPTH - step);
|
||||||
bool left_empty = neighbor_empty(glyph, gx, gy, -1, 0);
|
bool left_empty = neighbor_empty(glyph, gx, gy, -1, 0);
|
||||||
@@ -303,38 +198,29 @@ static void render_char_advanced(char c, float offset_x, const RotationState *ro
|
|||||||
bool top_empty = neighbor_empty(glyph, gx, gy, 0, -1);
|
bool top_empty = neighbor_empty(glyph, gx, gy, 0, -1);
|
||||||
bool bottom_empty = neighbor_empty(glyph, gx, gy, 0, 1);
|
bool bottom_empty = neighbor_empty(glyph, gx, gy, 0, 1);
|
||||||
|
|
||||||
bool is_surface = is_front || is_back ||
|
bool is_surface = is_front || is_back || left_empty || right_empty ||
|
||||||
left_empty || right_empty ||
|
|
||||||
top_empty || bottom_empty;
|
top_empty || bottom_empty;
|
||||||
|
|
||||||
/* Wireframe mode: only render edges */
|
|
||||||
if (g_render_mode == RENDER_MODE_WIREFRAME) {
|
if (g_render_mode == RENDER_MODE_WIREFRAME) {
|
||||||
int edge_count = (is_front ? 1 : 0) + (is_back ? 1 : 0) +
|
int edge_count = (is_front ? 1 : 0) + (is_back ? 1 : 0) +
|
||||||
(left_empty ? 1 : 0) + (right_empty ? 1 : 0) +
|
(left_empty ? 1 : 0) + (right_empty ? 1 : 0) +
|
||||||
(top_empty ? 1 : 0) + (bottom_empty ? 1 : 0);
|
(top_empty ? 1 : 0) + (bottom_empty ? 1 : 0);
|
||||||
if (edge_count < 2) continue;
|
if (edge_count < 2)
|
||||||
}
|
|
||||||
|
|
||||||
/* Points mode: sparse rendering */
|
|
||||||
if (g_render_mode == RENDER_MODE_POINTS) {
|
|
||||||
if (!is_front && !is_back) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_surface && g_render_mode != RENDER_MODE_POINTS) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create 3D point centered at origin */
|
if (g_render_mode == RENDER_MODE_POINTS && (!is_front && !is_back))
|
||||||
Vec3 point = vec3_create(
|
continue;
|
||||||
((float)gx - cx) * CHAR_SCALE + offset_x,
|
if (!is_surface && g_render_mode != RENDER_MODE_POINTS)
|
||||||
(cy - (float)gy) * CHAR_SCALE,
|
continue;
|
||||||
(gz - cz) * CHAR_SCALE
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Calculate smooth normal for better shading */
|
Vec3 point =
|
||||||
Vec3 normal = calculate_smooth_normal(glyph, gx, gy, gz, is_front, is_back);
|
vec3_create(((float)gx - cx) * CHAR_SCALE + offset_x,
|
||||||
|
(cy - (float)gy) * CHAR_SCALE, (gz - cz) * CHAR_SCALE);
|
||||||
|
|
||||||
|
Vec3 normal =
|
||||||
|
calculate_smooth_normal(glyph, gx, gy, gz, is_front, is_back);
|
||||||
|
|
||||||
/* Apply rotations */
|
|
||||||
if (rotation->enable_x) {
|
if (rotation->enable_x) {
|
||||||
point = vec3_rotate_x(point, rotation->angle_x);
|
point = vec3_rotate_x(point, rotation->angle_x);
|
||||||
normal = vec3_rotate_x(normal, rotation->angle_x);
|
normal = vec3_rotate_x(normal, rotation->angle_x);
|
||||||
@@ -348,16 +234,15 @@ static void render_char_advanced(char c, float offset_x, const RotationState *ro
|
|||||||
normal = vec3_rotate_z(normal, rotation->angle_z);
|
normal = vec3_rotate_z(normal, rotation->angle_z);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Project and render */
|
|
||||||
float sx, sy, sz;
|
float sx, sy, sz;
|
||||||
if (project_point(point, &sx, &sy, &sz)) {
|
if (project_point(point, &sx, &sy, &sz)) {
|
||||||
if (settings->anti_aliasing) {
|
if (settings->anti_aliasing) {
|
||||||
/* Multi-sample AA */
|
|
||||||
for (int aay = 0; aay < AA_SAMPLES; aay++) {
|
for (int aay = 0; aay < AA_SAMPLES; aay++) {
|
||||||
for (int aax = 0; aax < AA_SAMPLES; aax++) {
|
for (int aax = 0; aax < AA_SAMPLES; aax++) {
|
||||||
float ox = (float)aax / (float)AA_SAMPLES - 0.5f;
|
float ox = (float)aax / (float)AA_SAMPLES - 0.5f;
|
||||||
float oy = (float)aay / (float)AA_SAMPLES - 0.5f;
|
float oy = (float)aay / (float)AA_SAMPLES - 0.5f;
|
||||||
plot_point_aa(sx + ox * 0.5f, sy + oy * 0.5f, sz, point, normal);
|
plot_point_aa(sx + ox * 0.5f, sy + oy * 0.5f, sz, point,
|
||||||
|
normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -369,40 +254,24 @@ static void render_char_advanced(char c, float offset_x, const RotationState *ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*============================================================================
|
int renderer_init(void) {
|
||||||
* PUBLIC API
|
|
||||||
*============================================================================*/
|
|
||||||
|
|
||||||
int renderer_init(void)
|
|
||||||
{
|
|
||||||
/* Initialize lighting system */
|
|
||||||
lighting_init(&g_lighting);
|
lighting_init(&g_lighting);
|
||||||
|
|
||||||
/* Initialize default material */
|
|
||||||
g_material = lighting_default_material();
|
g_material = lighting_default_material();
|
||||||
g_material.diffuse = color_create(0.9f, 0.9f, 0.95f);
|
g_material.diffuse = color_create(0.9f, 0.9f, 0.95f);
|
||||||
g_material.specular = color_create(1.0f, 1.0f, 1.0f);
|
g_material.specular = color_create(1.0f, 1.0f, 1.0f);
|
||||||
g_material.shininess = 64.0f;
|
g_material.shininess = 64.0f;
|
||||||
|
|
||||||
/* Initialize settings */
|
|
||||||
g_settings = renderer_default_settings();
|
g_settings = renderer_default_settings();
|
||||||
|
|
||||||
/* Clear buffers */
|
|
||||||
renderer_clear();
|
renderer_clear();
|
||||||
|
|
||||||
/* Initialize stats */
|
|
||||||
memset(&g_stats, 0, sizeof(g_stats));
|
memset(&g_stats, 0, sizeof(g_stats));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_cleanup(void)
|
void renderer_cleanup(void) {}
|
||||||
{
|
|
||||||
/* Nothing to clean up */
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderer_clear(void)
|
void renderer_clear(void) {
|
||||||
{
|
|
||||||
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
memset(g_screen[y], ' ', SCREEN_WIDTH);
|
memset(g_screen[y], ' ', SCREEN_WIDTH);
|
||||||
g_screen[y][SCREEN_WIDTH] = '\0';
|
g_screen[y][SCREEN_WIDTH] = '\0';
|
||||||
@@ -418,38 +287,31 @@ void renderer_clear(void)
|
|||||||
g_voxel_count = 0;
|
g_voxel_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_draw_text(const char *text, const RotationState *rotation)
|
void renderer_draw_text(const char *text, const RotationState *rotation) {
|
||||||
{
|
|
||||||
renderer_draw_text_ex(text, rotation, &g_settings);
|
renderer_draw_text_ex(text, rotation, &g_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
||||||
const RenderSettings *settings)
|
const RenderSettings *settings) {
|
||||||
{
|
if (!text || !rotation || !settings)
|
||||||
if (text == NULL || rotation == NULL || settings == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
size_t len = strlen(text);
|
size_t len = strlen(text);
|
||||||
if (len == 0) {
|
if (len == 0)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate total width for centering */
|
|
||||||
float char_width = (float)(FONT_WIDTH + FONT_CHAR_SPACING) * CHAR_SCALE;
|
float char_width = (float)(FONT_WIDTH + FONT_CHAR_SPACING) * CHAR_SCALE;
|
||||||
float total_width = (float)len * char_width;
|
float total_width = (float)len * char_width;
|
||||||
float start_x = -total_width / 2.0f + char_width / 2.0f;
|
float start_x = -total_width / 2.0f + char_width / 2.0f;
|
||||||
|
|
||||||
/* Render each character */
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
for (size_t i = 0; i < len; i++) {
|
||||||
if (text[i] == ' ') {
|
if (text[i] == ' ')
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
float offset_x = start_x + (float)i * char_width;
|
float offset_x = start_x + (float)i * char_width;
|
||||||
render_char_advanced(text[i], offset_x, rotation, settings);
|
render_char_advanced(text[i], offset_x, rotation, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finalize AA samples */
|
// Perform sub-pixel reduction averages across accumulated geometry
|
||||||
if (settings->anti_aliasing) {
|
if (settings->anti_aliasing) {
|
||||||
const char *palette = g_palettes[g_current_palette];
|
const char *palette = g_palettes[g_current_palette];
|
||||||
int palette_size = g_palette_sizes[g_current_palette];
|
int palette_size = g_palette_sizes[g_current_palette];
|
||||||
@@ -457,9 +319,14 @@ void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
|||||||
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
if (g_sample_count[y][x] > 0) {
|
if (g_sample_count[y][x] > 0) {
|
||||||
float avg_intensity = g_intensity_buffer[y][x] / (float)g_sample_count[y][x];
|
float avg_intensity =
|
||||||
int shade_idx = (int)(avg_intensity * (float)(palette_size - 1) + 0.5f);
|
g_intensity_buffer[y][x] / (float)g_sample_count[y][x];
|
||||||
shade_idx = shade_idx < 0 ? 0 : (shade_idx >= palette_size ? palette_size - 1 : shade_idx);
|
int shade_idx =
|
||||||
|
(int)(avg_intensity * (float)(palette_size - 1) + 0.5f);
|
||||||
|
shade_idx =
|
||||||
|
shade_idx < 0
|
||||||
|
? 0
|
||||||
|
: (shade_idx >= palette_size ? palette_size - 1 : shade_idx);
|
||||||
g_screen[y][x] = palette[shade_idx];
|
g_screen[y][x] = palette[shade_idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,28 +336,21 @@ void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
|||||||
g_stats.triangles = g_voxel_count;
|
g_stats.triangles = g_voxel_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_present(void)
|
void renderer_present(void) {
|
||||||
{
|
|
||||||
/* Move cursor to home position */
|
|
||||||
printf("\033[H");
|
printf("\033[H");
|
||||||
|
|
||||||
/* Output screen buffer */
|
|
||||||
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
printf("%s\n", g_screen[y]);
|
printf("%s\n", g_screen[y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_present_color(const RenderSettings *settings)
|
void renderer_present_color(const RenderSettings *settings) {
|
||||||
{
|
if (!settings || settings->color_mode == COLOR_MODE_MONO) {
|
||||||
if (settings == NULL || settings->color_mode == COLOR_MODE_MONO) {
|
|
||||||
renderer_present();
|
renderer_present();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\033[H");
|
printf("\033[H");
|
||||||
|
|
||||||
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
char c = g_screen[y][x];
|
char c = g_screen[y][x];
|
||||||
@@ -503,7 +363,6 @@ void renderer_present_color(const RenderSettings *settings)
|
|||||||
Color col = g_color_buffer[y][x];
|
Color col = g_color_buffer[y][x];
|
||||||
|
|
||||||
if (settings->color_mode == COLOR_MODE_TRUECOLOR) {
|
if (settings->color_mode == COLOR_MODE_TRUECOLOR) {
|
||||||
/* 24-bit truecolor */
|
|
||||||
int r = (int)(col.r * 255.0f);
|
int r = (int)(col.r * 255.0f);
|
||||||
int g = (int)(col.g * 255.0f);
|
int g = (int)(col.g * 255.0f);
|
||||||
int b = (int)(col.b * 255.0f);
|
int b = (int)(col.b * 255.0f);
|
||||||
@@ -512,7 +371,6 @@ void renderer_present_color(const RenderSettings *settings)
|
|||||||
b = b < 0 ? 0 : (b > 255 ? 255 : b);
|
b = b < 0 ? 0 : (b > 255 ? 255 : b);
|
||||||
printf("\033[38;2;%d;%d;%dm%c", r, g, b, c);
|
printf("\033[38;2;%d;%d;%dm%c", r, g, b, c);
|
||||||
} else if (settings->color_mode == COLOR_MODE_ANSI_256) {
|
} else if (settings->color_mode == COLOR_MODE_ANSI_256) {
|
||||||
/* 256-color mode - approximate */
|
|
||||||
int r = (int)(col.r * 5.0f);
|
int r = (int)(col.r * 5.0f);
|
||||||
int g_val = (int)(col.g * 5.0f);
|
int g_val = (int)(col.g * 5.0f);
|
||||||
int b = (int)(col.b * 5.0f);
|
int b = (int)(col.b * 5.0f);
|
||||||
@@ -522,84 +380,72 @@ void renderer_present_color(const RenderSettings *settings)
|
|||||||
int color_code = 16 + 36 * r + 6 * g_val + b;
|
int color_code = 16 + 36 * r + 6 * g_val + b;
|
||||||
printf("\033[38;5;%dm%c", color_code, c);
|
printf("\033[38;5;%dm%c", color_code, c);
|
||||||
} else {
|
} else {
|
||||||
/* 16-color ANSI */
|
|
||||||
float intensity = color_to_intensity(col);
|
float intensity = color_to_intensity(col);
|
||||||
int bright = intensity > 0.5f ? 1 : 0;
|
int bright = intensity > 0.5f ? 1 : 0;
|
||||||
int base = 30;
|
int base = 30;
|
||||||
|
|
||||||
/* Simple color mapping */
|
if (col.r > col.g && col.r > col.b)
|
||||||
if (col.r > col.g && col.r > col.b) {
|
base = 31;
|
||||||
base = 31; /* Red */
|
else if (col.g > col.r && col.g > col.b)
|
||||||
} else if (col.g > col.r && col.g > col.b) {
|
base = 32;
|
||||||
base = 32; /* Green */
|
else if (col.b > col.r && col.b > col.g)
|
||||||
} else if (col.b > col.r && col.b > col.g) {
|
base = 34;
|
||||||
base = 34; /* Blue */
|
else if (col.r > 0.5f && col.g > 0.5f)
|
||||||
} else if (col.r > 0.5f && col.g > 0.5f) {
|
base = 33;
|
||||||
base = 33; /* Yellow */
|
else if (col.r > 0.5f && col.b > 0.5f)
|
||||||
} else if (col.r > 0.5f && col.b > 0.5f) {
|
base = 35;
|
||||||
base = 35; /* Magenta */
|
else if (col.g > 0.5f && col.b > 0.5f)
|
||||||
} else if (col.g > 0.5f && col.b > 0.5f) {
|
base = 36;
|
||||||
base = 36; /* Cyan */
|
else
|
||||||
} else {
|
base = 37;
|
||||||
base = 37; /* White */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bright) {
|
if (bright)
|
||||||
printf("\033[1;%dm%c", base, c);
|
printf("\033[1;%dm%c", base, c);
|
||||||
} else {
|
else
|
||||||
printf("\033[%dm%c", base, c);
|
printf("\033[%dm%c", base, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
printf("\033[0m\n");
|
||||||
printf(ANSI_RESET "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_update_rotation(RotationState *rotation, double delta_time)
|
void renderer_update_rotation(RotationState *rotation, double delta_time) {
|
||||||
{
|
if (!rotation)
|
||||||
if (rotation == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
float dt = (float)delta_time * rotation->speed;
|
float dt = (float)delta_time * rotation->speed;
|
||||||
|
|
||||||
if (rotation->enable_x) {
|
if (rotation->enable_x)
|
||||||
rotation->angle_x += dt * 0.7f;
|
rotation->angle_x += dt * 0.7f;
|
||||||
}
|
if (rotation->enable_y)
|
||||||
if (rotation->enable_y) {
|
|
||||||
rotation->angle_y += dt * 1.0f;
|
rotation->angle_y += dt * 1.0f;
|
||||||
}
|
if (rotation->enable_z)
|
||||||
if (rotation->enable_z) {
|
|
||||||
rotation->angle_z += dt * 0.5f;
|
rotation->angle_z += dt * 0.5f;
|
||||||
}
|
|
||||||
|
|
||||||
/* Keep angles in reasonable range */
|
|
||||||
const float TWO_PI = 6.283185307f;
|
const float TWO_PI = 6.283185307f;
|
||||||
if (rotation->angle_x > TWO_PI) rotation->angle_x -= TWO_PI;
|
if (rotation->angle_x > TWO_PI)
|
||||||
if (rotation->angle_y > TWO_PI) rotation->angle_y -= TWO_PI;
|
rotation->angle_x -= TWO_PI;
|
||||||
if (rotation->angle_z > TWO_PI) rotation->angle_z -= TWO_PI;
|
if (rotation->angle_y > TWO_PI)
|
||||||
|
rotation->angle_y -= TWO_PI;
|
||||||
|
if (rotation->angle_z > TWO_PI)
|
||||||
|
rotation->angle_z -= TWO_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
RotationState renderer_default_rotation(void)
|
RotationState renderer_default_rotation(void) {
|
||||||
{
|
RotationState state = {.angle_x = 0.0f,
|
||||||
RotationState state = {
|
|
||||||
.angle_x = 0.0f,
|
|
||||||
.angle_y = 0.0f,
|
.angle_y = 0.0f,
|
||||||
.angle_z = 0.0f,
|
.angle_z = 0.0f,
|
||||||
.enable_x = false,
|
.enable_x = false,
|
||||||
.enable_y = true,
|
.enable_y = true,
|
||||||
.enable_z = false,
|
.enable_z = false,
|
||||||
.speed = 1.0f
|
.speed = 1.0f};
|
||||||
};
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderSettings renderer_default_settings(void)
|
RenderSettings renderer_default_settings(void) {
|
||||||
{
|
RenderSettings settings = {.mode = RENDER_MODE_SHADED,
|
||||||
RenderSettings settings = {
|
|
||||||
.mode = RENDER_MODE_SHADED,
|
|
||||||
.color_mode = COLOR_MODE_MONO,
|
.color_mode = COLOR_MODE_MONO,
|
||||||
.anti_aliasing = false,
|
.anti_aliasing = false,
|
||||||
.show_fps = false,
|
.show_fps = false,
|
||||||
@@ -607,71 +453,56 @@ RenderSettings renderer_default_settings(void)
|
|||||||
.quality = 1.0f,
|
.quality = 1.0f,
|
||||||
.palette_index = 1,
|
.palette_index = 1,
|
||||||
.base_color = color_create(1.0f, 1.0f, 1.0f),
|
.base_color = color_create(1.0f, 1.0f, 1.0f),
|
||||||
.highlight_color = color_create(1.0f, 1.0f, 1.0f)
|
.highlight_color = color_create(1.0f, 1.0f, 1.0f)};
|
||||||
};
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_set_mode(RenderMode mode)
|
void renderer_set_mode(RenderMode mode) {
|
||||||
{
|
|
||||||
if (mode < RENDER_MODE_COUNT) {
|
if (mode < RENDER_MODE_COUNT) {
|
||||||
g_render_mode = mode;
|
g_render_mode = mode;
|
||||||
g_settings.mode = mode;
|
g_settings.mode = mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_set_color_mode(ColorMode mode)
|
void renderer_set_color_mode(ColorMode mode) {
|
||||||
{
|
|
||||||
if (mode < COLOR_MODE_COUNT) {
|
if (mode < COLOR_MODE_COUNT) {
|
||||||
g_color_mode = mode;
|
g_color_mode = mode;
|
||||||
g_settings.color_mode = mode;
|
g_settings.color_mode = mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameStats renderer_get_stats(void)
|
FrameStats renderer_get_stats(void) { return g_stats; }
|
||||||
{
|
|
||||||
return g_stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderer_set_palette(int palette_index)
|
void renderer_set_palette(int palette_index) {
|
||||||
{
|
|
||||||
if (palette_index >= 0 && palette_index < 4) {
|
if (palette_index >= 0 && palette_index < 4) {
|
||||||
g_current_palette = palette_index;
|
g_current_palette = palette_index;
|
||||||
g_settings.palette_index = palette_index;
|
g_settings.palette_index = palette_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LightingSystem *renderer_get_lighting(void)
|
LightingSystem *renderer_get_lighting(void) { return &g_lighting; }
|
||||||
{
|
|
||||||
return &g_lighting;
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderer_hide_cursor(void)
|
void renderer_hide_cursor(void) {
|
||||||
{
|
|
||||||
printf("\033[?25l");
|
printf("\033[?25l");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_show_cursor(void)
|
void renderer_show_cursor(void) {
|
||||||
{
|
|
||||||
printf("\033[?25h");
|
printf("\033[?25h");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_clear_terminal(void)
|
void renderer_clear_terminal(void) {
|
||||||
{
|
|
||||||
printf("\033[2J");
|
printf("\033[2J");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_enter_alternate_screen(void)
|
void renderer_enter_alternate_screen(void) {
|
||||||
{
|
|
||||||
printf("\033[?1049h");
|
printf("\033[?1049h");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderer_exit_alternate_screen(void)
|
void renderer_exit_alternate_screen(void) {
|
||||||
{
|
|
||||||
printf("\033[?1049l");
|
printf("\033[?1049l");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/timing.c
20
src/timing.c
@@ -1,33 +1,24 @@
|
|||||||
/**
|
|
||||||
* @file timing.c
|
|
||||||
* @brief High-precision timing utilities implementation
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
double timing_get_seconds(void)
|
double timing_get_seconds(void) {
|
||||||
{
|
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
// We enforce monotonic clock so system daylight saving updates don't break
|
||||||
|
// delta logic
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
return (double)ts.tv_sec + (double)ts.tv_nsec / 1.0e9;
|
return (double)ts.tv_sec + (double)ts.tv_nsec / 1.0e9;
|
||||||
}
|
}
|
||||||
|
|
||||||
void timing_sleep_us(unsigned int microseconds)
|
void timing_sleep_us(unsigned int microseconds) {
|
||||||
{
|
|
||||||
struct timespec req;
|
struct timespec req;
|
||||||
req.tv_sec = microseconds / 1000000;
|
req.tv_sec = microseconds / 1000000;
|
||||||
req.tv_nsec = (microseconds % 1000000) * 1000L;
|
req.tv_nsec = (microseconds % 1000000) * 1000L;
|
||||||
nanosleep(&req, NULL);
|
nanosleep(&req, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timing_limit_fps(double frame_start_time, int target_fps)
|
void timing_limit_fps(double frame_start_time, int target_fps) {
|
||||||
{
|
|
||||||
if (target_fps <= 0) {
|
if (target_fps <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -36,6 +27,7 @@ void timing_limit_fps(double frame_start_time, int target_fps)
|
|||||||
double elapsed = timing_get_seconds() - frame_start_time;
|
double elapsed = timing_get_seconds() - frame_start_time;
|
||||||
double sleep_time = target_frame_time - elapsed;
|
double sleep_time = target_frame_time - elapsed;
|
||||||
|
|
||||||
|
// Attempt dynamic throttle
|
||||||
if (sleep_time > 0.0) {
|
if (sleep_time > 0.0) {
|
||||||
unsigned int sleep_us = (unsigned int)(sleep_time * 1.0e6);
|
unsigned int sleep_us = (unsigned int)(sleep_time * 1.0e6);
|
||||||
timing_sleep_us(sleep_us);
|
timing_sleep_us(sleep_us);
|
||||||
|
|||||||
98
src/tui.c
Normal file
98
src/tui.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "tui.h"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static struct termios original_termios;
|
||||||
|
static int is_tui_initialized = 0;
|
||||||
|
|
||||||
|
int tui_init(void) {
|
||||||
|
if (tcgetattr(STDIN_FILENO, &original_termios) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct termios raw = original_termios;
|
||||||
|
// We disable canonical mode and local echo so input is available instantly
|
||||||
|
// and invisible to the user until we handle it
|
||||||
|
raw.c_lflag &= ~(unsigned int)(ICANON | ECHO);
|
||||||
|
|
||||||
|
if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch STDIN to non-blocking mode.
|
||||||
|
// If no keys are pressed, read() returns immediately without halting
|
||||||
|
// rendering.
|
||||||
|
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
is_tui_initialized = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tui_cleanup(void) {
|
||||||
|
if (is_tui_initialized) {
|
||||||
|
// Re-enable canonical mode/echo and block reads
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &original_termios);
|
||||||
|
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tui_process_input(RotationState *rotation, RenderSettings *settings,
|
||||||
|
char *text_buffer, size_t max_text_len) {
|
||||||
|
char c;
|
||||||
|
while (read(STDIN_FILENO, &c, 1) == 1) {
|
||||||
|
switch (c) {
|
||||||
|
case 27: // Physical ESC key
|
||||||
|
case 'q':
|
||||||
|
return 1;
|
||||||
|
case ' ': // Space bars act as play/pause for auto rotation
|
||||||
|
settings->auto_rotate = !settings->auto_rotate;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
settings->mode = (RenderMode)((settings->mode + 1) % RENDER_MODE_COUNT);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
settings->color_mode =
|
||||||
|
(ColorMode)((settings->color_mode + 1) % COLOR_MODE_COUNT);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
// Currently 4 palettes defined in our system
|
||||||
|
settings->palette_index = (settings->palette_index + 1) % 4;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
rotation->speed += 0.2f;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
rotation->speed -= 0.2f;
|
||||||
|
if (rotation->speed < 0.0f) {
|
||||||
|
rotation->speed = 0.0f;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 127: // Backspace (DEL character)
|
||||||
|
case 8: // Backspace (BS character)
|
||||||
|
{
|
||||||
|
size_t len = strlen(text_buffer);
|
||||||
|
if (len > 0) {
|
||||||
|
text_buffer[len - 1] = '\0';
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
// Only allow standard displayable ASCII.
|
||||||
|
// It makes the TUI feel more like a dynamic terminal app
|
||||||
|
if (c >= ' ' && c <= '~') {
|
||||||
|
size_t len = strlen(text_buffer);
|
||||||
|
if (len < max_text_len - 1) {
|
||||||
|
text_buffer[len] = c;
|
||||||
|
text_buffer[len + 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
75
src/vec3.c
75
src/vec3.c
@@ -1,91 +1,58 @@
|
|||||||
/**
|
|
||||||
* @file vec3.c
|
|
||||||
* @brief 3D Vector mathematics implementation
|
|
||||||
* @author ASCII3D Project
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "vec3.h"
|
#include "vec3.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
Vec3 vec3_create(float x, float y, float z)
|
// Vector Math Library
|
||||||
{
|
// Optimized for simple local 3D transformations
|
||||||
|
|
||||||
|
Vec3 vec3_create(float x, float y, float z) {
|
||||||
Vec3 v = {x, y, z};
|
Vec3 v = {x, y, z};
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_add(Vec3 a, Vec3 b)
|
Vec3 vec3_add(Vec3 a, Vec3 b) {
|
||||||
{
|
|
||||||
return vec3_create(a.x + b.x, a.y + b.y, a.z + b.z);
|
return vec3_create(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_sub(Vec3 a, Vec3 b)
|
Vec3 vec3_sub(Vec3 a, Vec3 b) {
|
||||||
{
|
|
||||||
return vec3_create(a.x - b.x, a.y - b.y, a.z - b.z);
|
return vec3_create(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_scale(Vec3 v, float s)
|
Vec3 vec3_scale(Vec3 v, float s) {
|
||||||
{
|
|
||||||
return vec3_create(v.x * s, v.y * s, v.z * s);
|
return vec3_create(v.x * s, v.y * s, v.z * s);
|
||||||
}
|
}
|
||||||
|
|
||||||
float vec3_dot(Vec3 a, Vec3 b)
|
float vec3_dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
|
||||||
{
|
|
||||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
Vec3 vec3_cross(Vec3 a, Vec3 b) {
|
||||||
|
return vec3_create(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
|
||||||
|
a.x * b.y - a.y * b.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_cross(Vec3 a, Vec3 b)
|
float vec3_length(Vec3 v) { return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); }
|
||||||
{
|
|
||||||
return vec3_create(
|
|
||||||
a.y * b.z - a.z * b.y,
|
|
||||||
a.z * b.x - a.x * b.z,
|
|
||||||
a.x * b.y - a.y * b.x
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
float vec3_length(Vec3 v)
|
Vec3 vec3_normalize(Vec3 v) {
|
||||||
{
|
|
||||||
return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 vec3_normalize(Vec3 v)
|
|
||||||
{
|
|
||||||
float len = vec3_length(v);
|
float len = vec3_length(v);
|
||||||
|
// Arbitrarily high epsilon cut-off to avoid division by zero artifacts
|
||||||
if (len > 0.0001f) {
|
if (len > 0.0001f) {
|
||||||
return vec3_scale(v, 1.0f / len);
|
return vec3_scale(v, 1.0f / len);
|
||||||
}
|
}
|
||||||
return vec3_create(0.0f, 0.0f, 0.0f);
|
return vec3_create(0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_rotate_x(Vec3 v, float angle)
|
Vec3 vec3_rotate_x(Vec3 v, float angle) {
|
||||||
{
|
|
||||||
float c = cosf(angle);
|
float c = cosf(angle);
|
||||||
float s = sinf(angle);
|
float s = sinf(angle);
|
||||||
return vec3_create(
|
return vec3_create(v.x, v.y * c - v.z * s, v.y * s + v.z * c);
|
||||||
v.x,
|
|
||||||
v.y * c - v.z * s,
|
|
||||||
v.y * s + v.z * c
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_rotate_y(Vec3 v, float angle)
|
Vec3 vec3_rotate_y(Vec3 v, float angle) {
|
||||||
{
|
|
||||||
float c = cosf(angle);
|
float c = cosf(angle);
|
||||||
float s = sinf(angle);
|
float s = sinf(angle);
|
||||||
return vec3_create(
|
return vec3_create(v.x * c + v.z * s, v.y, -v.x * s + v.z * c);
|
||||||
v.x * c + v.z * s,
|
|
||||||
v.y,
|
|
||||||
-v.x * s + v.z * c
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 vec3_rotate_z(Vec3 v, float angle)
|
Vec3 vec3_rotate_z(Vec3 v, float angle) {
|
||||||
{
|
|
||||||
float c = cosf(angle);
|
float c = cosf(angle);
|
||||||
float s = sinf(angle);
|
float s = sinf(angle);
|
||||||
return vec3_create(
|
return vec3_create(v.x * c - v.y * s, v.x * s + v.y * c, v.z);
|
||||||
v.x * c - v.y * s,
|
|
||||||
v.x * s + v.y * c,
|
|
||||||
v.z
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user