(Feat): Initial Commit.
This commit is contained in:
5
.clangd
Normal file
5
.clangd
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
CompileFlags:
|
||||||
|
Add:
|
||||||
|
- -xc
|
||||||
|
- -std=c11
|
||||||
|
- -I/home/zanewalker/Development/Programming/C_Projects/ASCIIRenderer/include
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.qodo
|
||||||
|
.vscode/
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
*.o
|
||||||
|
*.out
|
||||||
|
*.log
|
||||||
125
Makefile
Normal file
125
Makefile
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
SRCDIR := src
|
||||||
|
INCDIR := include
|
||||||
|
OBJDIR := obj
|
||||||
|
BINDIR := bin
|
||||||
|
|
||||||
|
# Compiler and flags
|
||||||
|
CC := clang
|
||||||
|
CFLAGS := -std=c11 -Wall -Wextra -Wpedantic -Werror
|
||||||
|
CFLAGS += -Wshadow -Wconversion -Wdouble-promotion
|
||||||
|
CFLAGS += -Wformat=2 -Wundef -fno-common
|
||||||
|
CFLAGS += -I$(INCDIR)
|
||||||
|
|
||||||
|
# Target
|
||||||
|
TARGET := $(BINDIR)/ascii3d
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
SRCS := $(wildcard $(SRCDIR)/*.c)
|
||||||
|
OBJS := $(SRCS:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
|
||||||
|
DEPS := $(OBJS:.o=.d)
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
LDFLAGS := -lm
|
||||||
|
|
||||||
|
# Build modes
|
||||||
|
.PHONY: all debug release clean install uninstall help
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: release
|
||||||
|
|
||||||
|
# Release build (optimized)
|
||||||
|
release: CFLAGS += -O3 -DNDEBUG -march=native -flto
|
||||||
|
release: LDFLAGS += -flto
|
||||||
|
release: $(TARGET)
|
||||||
|
|
||||||
|
# Debug build (with symbols and sanitizers)
|
||||||
|
debug: CFLAGS += -O0 -g3 -DDEBUG
|
||||||
|
debug: CFLAGS += -fsanitize=address,undefined
|
||||||
|
debug: LDFLAGS += -fsanitize=address,undefined
|
||||||
|
debug: $(TARGET)
|
||||||
|
|
||||||
|
# Profile build (optimized with debug symbols)
|
||||||
|
profile: CFLAGS += -O2 -g -pg
|
||||||
|
profile: LDFLAGS += -pg
|
||||||
|
profile: $(TARGET)
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
$(OBJDIR) $(BINDIR):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
# Link
|
||||||
|
$(TARGET): $(OBJS) | $(BINDIR)
|
||||||
|
@echo "Linking $@..."
|
||||||
|
@$(CC) $(OBJS) -o $@ $(LDFLAGS)
|
||||||
|
@echo "Build complete: $@"
|
||||||
|
|
||||||
|
# Compile
|
||||||
|
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
|
||||||
|
@echo "Compiling $<..."
|
||||||
|
@$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
|
# Include dependencies
|
||||||
|
-include $(DEPS)
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning..."
|
||||||
|
@rm -rf $(OBJDIR) $(BINDIR)
|
||||||
|
@echo "Clean complete."
|
||||||
|
|
||||||
|
# Install to system
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
install: release
|
||||||
|
@echo "Installing to $(PREFIX)/bin..."
|
||||||
|
@install -d $(PREFIX)/bin
|
||||||
|
@install -m 755 $(TARGET) $(PREFIX)/bin/
|
||||||
|
@echo "Installation complete."
|
||||||
|
|
||||||
|
# Uninstall from system
|
||||||
|
uninstall:
|
||||||
|
@echo "Uninstalling from $(PREFIX)/bin..."
|
||||||
|
@rm -f $(PREFIX)/bin/ascii3d
|
||||||
|
@echo "Uninstallation complete."
|
||||||
|
|
||||||
|
# Run the program
|
||||||
|
run: release
|
||||||
|
@./$(TARGET)
|
||||||
|
|
||||||
|
# Run with demo text
|
||||||
|
demo: release
|
||||||
|
@./$(TARGET) -a HELLO
|
||||||
|
|
||||||
|
# Static analysis
|
||||||
|
analyze:
|
||||||
|
@echo "Running static analysis..."
|
||||||
|
@cppcheck --enable=all --std=c11 -I$(INCDIR) $(SRCDIR)/*.c
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
format:
|
||||||
|
@echo "Formatting code..."
|
||||||
|
@clang-format -i $(SRCDIR)/*.c $(INCDIR)/*.h
|
||||||
|
|
||||||
|
# Help
|
||||||
|
help:
|
||||||
|
@echo "ASCII 3D Renderer - Build System"
|
||||||
|
@echo "================================"
|
||||||
|
@echo ""
|
||||||
|
@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"
|
||||||
239
README.md
Normal file
239
README.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# ASCII 3D Renderer v2.0
|
||||||
|
|
||||||
|
An advanced, high-quality ASCII 3D text renderer written in C for fun.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Rendering
|
||||||
|
|
||||||
|
- **Phong Lighting Model** - Realistic lighting with ambient, diffuse, and specular components
|
||||||
|
- **Multiple Light Sources** - Key light, fill light, and rim light for professional 3-point lighting
|
||||||
|
- **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
|
||||||
|
|
||||||
|
- **Extended ASCII Palettes** - 70-level gradient for detailed shading
|
||||||
|
- **Block Characters** - Unicode block shading option (░▒▓█)
|
||||||
|
- **ANSI Color Support** - 16-color, 256-color, and 24-bit truecolor modes
|
||||||
|
- **Configurable Quality** - Adjustable voxel density for performance/quality tradeoff
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- **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
|
||||||
|
# Build release version
|
||||||
|
make
|
||||||
|
|
||||||
|
# Run with default text
|
||||||
|
./bin/ascii3d
|
||||||
|
|
||||||
|
# Run with custom text
|
||||||
|
./bin/ascii3d HELLO
|
||||||
|
|
||||||
|
# Truecolor with all-axis rotation
|
||||||
|
./bin/ascii3d -a -c3 WORLD
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Targets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make release # Optimized build (default)
|
||||||
|
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
|
||||||
|
|
||||||
|
# Fast wireframe mode
|
||||||
|
./bin/ascii3d -m1 -s2 WIRE
|
||||||
|
|
||||||
|
# High quality with anti-aliasing
|
||||||
|
./bin/ascii3d -A -q1.5 -c2 HQ
|
||||||
|
|
||||||
|
# Block character style
|
||||||
|
./bin/ascii3d -p2 BLOCKS
|
||||||
|
|
||||||
|
# Show FPS counter
|
||||||
|
./bin/ascii3d -f -a TEST
|
||||||
|
|
||||||
|
# Slow, high quality render
|
||||||
|
./bin/ascii3d -s0.3 -q2 -A SMOOTH
|
||||||
|
```
|
||||||
|
|
||||||
|
Press `Ctrl+C` to exit.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```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
|
||||||
170
include/config.h
Normal file
170
include/config.h
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* @file config.h
|
||||||
|
* @brief Configuration constants for ASCII 3D Renderer
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 2.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASCII3D_CONFIG_H
|
||||||
|
#define ASCII3D_CONFIG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* SCREEN CONFIGURATION
|
||||||
|
*============================================================================*/
|
||||||
|
#define SCREEN_WIDTH 120
|
||||||
|
#define SCREEN_HEIGHT 45
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* RENDERING QUALITY SETTINGS
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* Depth buffer initialization value */
|
||||||
|
#define DEPTH_BUFFER_INIT 1e9f
|
||||||
|
|
||||||
|
/* Character extrusion depth (3D thickness) */
|
||||||
|
#define EXTRUSION_DEPTH 4.0f
|
||||||
|
|
||||||
|
/* Base character scale */
|
||||||
|
#define CHAR_SCALE 2.0f
|
||||||
|
|
||||||
|
/* Camera settings */
|
||||||
|
#define CAMERA_DISTANCE 30.0f
|
||||||
|
#define FIELD_OF_VIEW 50.0f
|
||||||
|
#define NEAR_PLANE 0.1f
|
||||||
|
#define FAR_PLANE 100.0f
|
||||||
|
|
||||||
|
/* Sub-pixel sampling for anti-aliasing (NxN samples per pixel) */
|
||||||
|
#define AA_SAMPLES 2
|
||||||
|
|
||||||
|
/* Voxel rendering step (smaller = higher quality, slower) */
|
||||||
|
#define VOXEL_STEP 0.15f
|
||||||
|
|
||||||
|
/* Surface smoothing iterations */
|
||||||
|
#define SMOOTH_PASSES 1
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* ANIMATION PARAMETERS
|
||||||
|
*============================================================================*/
|
||||||
|
#define TARGET_FPS 60
|
||||||
|
#define FRAME_TIME_US (1000000 / TARGET_FPS)
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* FONT CONFIGURATION
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* Standard 5x7 font */
|
||||||
|
#define FONT_WIDTH 5
|
||||||
|
#define FONT_HEIGHT 7
|
||||||
|
#define FONT_CHAR_SPACING 2
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* LIGHTING CONFIGURATION
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* Ambient light intensity (0.0 - 1.0) */
|
||||||
|
#define AMBIENT_INTENSITY 0.15f
|
||||||
|
|
||||||
|
/* Diffuse light intensity */
|
||||||
|
#define DIFFUSE_INTENSITY 0.70f
|
||||||
|
|
||||||
|
/* Specular light intensity */
|
||||||
|
#define SPECULAR_INTENSITY 0.40f
|
||||||
|
|
||||||
|
/* Specular shininess exponent */
|
||||||
|
#define SPECULAR_POWER 32.0f
|
||||||
|
|
||||||
|
/* Number of light sources */
|
||||||
|
#define MAX_LIGHTS 3
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* ASCII SHADING PALETTES
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* Standard gradient (10 levels) */
|
||||||
|
#define SHADE_CHARS_STANDARD " .:-=+*#%@"
|
||||||
|
#define SHADE_COUNT_STANDARD 10
|
||||||
|
|
||||||
|
/* Extended gradient (16 levels) - more detail */
|
||||||
|
#define SHADE_CHARS_EXTENDED " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
|
||||||
|
#define SHADE_COUNT_EXTENDED 70
|
||||||
|
|
||||||
|
/* Block characters for solid look */
|
||||||
|
#define SHADE_CHARS_BLOCK " ░▒▓█"
|
||||||
|
#define SHADE_COUNT_BLOCK 5
|
||||||
|
|
||||||
|
/* Minimal gradient */
|
||||||
|
#define SHADE_CHARS_MINIMAL " .:+#@"
|
||||||
|
#define SHADE_COUNT_MINIMAL 6
|
||||||
|
|
||||||
|
/* Default palette */
|
||||||
|
#define SHADE_CHARS SHADE_CHARS_EXTENDED
|
||||||
|
#define SHADE_COUNT SHADE_COUNT_EXTENDED
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* RENDER MODES
|
||||||
|
*============================================================================*/
|
||||||
|
typedef enum RenderMode {
|
||||||
|
RENDER_MODE_SOLID = 0, /* Filled solid rendering */
|
||||||
|
RENDER_MODE_WIREFRAME, /* Edge-only wireframe */
|
||||||
|
RENDER_MODE_POINTS, /* Point cloud */
|
||||||
|
RENDER_MODE_SHADED, /* Full Phong shading */
|
||||||
|
RENDER_MODE_COUNT
|
||||||
|
} RenderMode;
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* COLOR MODES
|
||||||
|
*============================================================================*/
|
||||||
|
typedef enum ColorMode {
|
||||||
|
COLOR_MODE_MONO = 0, /* Monochrome ASCII */
|
||||||
|
COLOR_MODE_ANSI_16, /* 16-color ANSI */
|
||||||
|
COLOR_MODE_ANSI_256, /* 256-color ANSI */
|
||||||
|
COLOR_MODE_TRUECOLOR, /* 24-bit RGB */
|
||||||
|
COLOR_MODE_COUNT
|
||||||
|
} ColorMode;
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* ANSI COLOR CODES
|
||||||
|
*============================================================================*/
|
||||||
|
#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
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ASCII3D_CONFIG_H */
|
||||||
44
include/font.h
Normal file
44
include/font.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* @file font.h
|
||||||
|
* @brief Bitmap font data for ASCII 3D Renderer
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASCII3D_FONT_H
|
||||||
|
#define ASCII3D_FONT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the font glyph data for a character
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if character is renderable
|
||||||
|
* @param c Character to check
|
||||||
|
* @return true if character can be rendered
|
||||||
|
*/
|
||||||
|
bool font_is_renderable(char c);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ASCII3D_FONT_H */
|
||||||
170
include/lighting.h
Normal file
170
include/lighting.h
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* @file lighting.h
|
||||||
|
* @brief Advanced lighting system for ASCII 3D Renderer
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 2.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASCII3D_LIGHTING_H
|
||||||
|
#define ASCII3D_LIGHTING_H
|
||||||
|
|
||||||
|
#include "vec3.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Light types
|
||||||
|
*/
|
||||||
|
typedef enum LightType {
|
||||||
|
LIGHT_DIRECTIONAL = 0, /* Parallel rays (sun-like) */
|
||||||
|
LIGHT_POINT, /* Point source with falloff */
|
||||||
|
LIGHT_SPOT /* Cone-shaped spotlight */
|
||||||
|
} LightType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RGB Color structure
|
||||||
|
*/
|
||||||
|
typedef struct Color {
|
||||||
|
float r, g, b;
|
||||||
|
} Color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Light source structure
|
||||||
|
*/
|
||||||
|
typedef struct Light {
|
||||||
|
LightType type;
|
||||||
|
Vec3 position; /* Position for point/spot lights */
|
||||||
|
Vec3 direction; /* Direction for directional/spot lights */
|
||||||
|
Color color; /* Light color */
|
||||||
|
float intensity; /* Light intensity multiplier */
|
||||||
|
float falloff; /* Attenuation for point lights */
|
||||||
|
float spot_angle; /* Cone angle for spotlights (radians) */
|
||||||
|
bool enabled;
|
||||||
|
} Light;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Material properties for surfaces
|
||||||
|
*/
|
||||||
|
typedef struct Material {
|
||||||
|
Color ambient; /* Ambient color */
|
||||||
|
Color diffuse; /* Diffuse color */
|
||||||
|
Color specular; /* Specular highlight color */
|
||||||
|
float shininess; /* Specular exponent */
|
||||||
|
float reflectivity; /* For future use */
|
||||||
|
} Material;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lighting system state
|
||||||
|
*/
|
||||||
|
typedef struct LightingSystem {
|
||||||
|
Light lights[MAX_LIGHTS];
|
||||||
|
int light_count;
|
||||||
|
Color ambient_color;
|
||||||
|
float ambient_intensity;
|
||||||
|
Vec3 camera_position;
|
||||||
|
} LightingSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the lighting system with default lights
|
||||||
|
* @param system Lighting system to initialize
|
||||||
|
*/
|
||||||
|
void lighting_init(LightingSystem *system);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a point light
|
||||||
|
* @param position Light position
|
||||||
|
* @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,
|
||||||
|
Vec3 normal, const Material *material);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create default material
|
||||||
|
* @return Default white material
|
||||||
|
*/
|
||||||
|
Material lighting_default_material(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiply color by scalar
|
||||||
|
*/
|
||||||
|
Color color_scale(Color c, float s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add two colors
|
||||||
|
*/
|
||||||
|
Color color_add(Color a, Color b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiply two colors component-wise
|
||||||
|
*/
|
||||||
|
Color color_multiply(Color a, Color b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clamp color components to 0-1 range
|
||||||
|
*/
|
||||||
|
Color color_clamp(Color c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert color to grayscale intensity
|
||||||
|
*/
|
||||||
|
float color_to_intensity(Color c);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ASCII3D_LIGHTING_H */
|
||||||
179
include/renderer.h
Normal file
179
include/renderer.h
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* @file renderer.h
|
||||||
|
* @brief Advanced rendering engine for ASCII 3D Renderer
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 2.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASCII3D_RENDERER_H
|
||||||
|
#define ASCII3D_RENDERER_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "lighting.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotation state for animation
|
||||||
|
*/
|
||||||
|
typedef struct RotationState {
|
||||||
|
float angle_x;
|
||||||
|
float angle_y;
|
||||||
|
float angle_z;
|
||||||
|
bool enable_x;
|
||||||
|
bool enable_y;
|
||||||
|
bool enable_z;
|
||||||
|
float speed;
|
||||||
|
} RotationState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Render settings structure
|
||||||
|
*/
|
||||||
|
typedef struct RenderSettings {
|
||||||
|
RenderMode mode; /* Rendering mode */
|
||||||
|
ColorMode color_mode; /* Color output mode */
|
||||||
|
bool anti_aliasing; /* Enable AA */
|
||||||
|
bool show_fps; /* Display FPS counter */
|
||||||
|
bool auto_rotate; /* Auto rotation enabled */
|
||||||
|
float quality; /* Quality multiplier (0.5 - 2.0) */
|
||||||
|
int palette_index; /* Shading palette selection */
|
||||||
|
Color base_color; /* Base color for colored modes */
|
||||||
|
Color highlight_color; /* Highlight/specular color */
|
||||||
|
} RenderSettings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frame statistics
|
||||||
|
*/
|
||||||
|
typedef struct FrameStats {
|
||||||
|
double frame_time; /* Last frame time in ms */
|
||||||
|
double fps; /* Current FPS */
|
||||||
|
double avg_fps; /* Average FPS */
|
||||||
|
int frame_count; /* Total frames rendered */
|
||||||
|
int triangles; /* Triangles/voxels rendered */
|
||||||
|
} FrameStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the renderer
|
||||||
|
* @return 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int renderer_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleanup renderer resources
|
||||||
|
*/
|
||||||
|
void renderer_cleanup(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the screen and depth buffers
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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,
|
||||||
|
const RenderSettings *settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Display the rendered frame to terminal
|
||||||
|
*/
|
||||||
|
void renderer_present(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Present with color support
|
||||||
|
* @param settings Render settings for color mode
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create default rotation state
|
||||||
|
* @return Default rotation state with Y-axis rotation enabled
|
||||||
|
*/
|
||||||
|
RotationState renderer_default_rotation(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create default render settings
|
||||||
|
* @return Default settings
|
||||||
|
*/
|
||||||
|
RenderSettings renderer_default_settings(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the render mode
|
||||||
|
* @param mode New render mode
|
||||||
|
*/
|
||||||
|
void renderer_set_mode(RenderMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the color mode
|
||||||
|
* @param mode New color 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the lighting system for modification
|
||||||
|
* @return Pointer to lighting system
|
||||||
|
*/
|
||||||
|
LightingSystem *renderer_get_lighting(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hide terminal cursor
|
||||||
|
*/
|
||||||
|
void renderer_hide_cursor(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show terminal cursor
|
||||||
|
*/
|
||||||
|
void renderer_show_cursor(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear terminal screen
|
||||||
|
*/
|
||||||
|
void renderer_clear_terminal(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set terminal to alternate screen buffer
|
||||||
|
*/
|
||||||
|
void renderer_enter_alternate_screen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Restore terminal to main screen buffer
|
||||||
|
*/
|
||||||
|
void renderer_exit_alternate_screen(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ASCII3D_RENDERER_H */
|
||||||
38
include/timing.h
Normal file
38
include/timing.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @file timing.h
|
||||||
|
* @brief High-precision timing utilities
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASCII3D_TIMING_H
|
||||||
|
#define ASCII3D_TIMING_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current time in seconds (monotonic clock)
|
||||||
|
* @return Time in seconds with nanosecond precision
|
||||||
|
*/
|
||||||
|
double timing_get_seconds(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sleep for specified microseconds
|
||||||
|
* @param microseconds Duration to sleep
|
||||||
|
*/
|
||||||
|
void timing_sleep_us(unsigned int microseconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frame rate limiter - sleeps to maintain target FPS
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ASCII3D_TIMING_H */
|
||||||
115
include/vec3.h
Normal file
115
include/vec3.h
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* @file vec3.h
|
||||||
|
* @brief 3D Vector mathematics for ASCII 3D Renderer
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASCII3D_VEC3_H
|
||||||
|
#define ASCII3D_VEC3_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 3D Vector structure
|
||||||
|
*/
|
||||||
|
typedef struct Vec3 {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add two vectors
|
||||||
|
* @param a First vector
|
||||||
|
* @param b Second vector
|
||||||
|
* @return Result of a + 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate length of a vector
|
||||||
|
* @param v Vector
|
||||||
|
* @return Length
|
||||||
|
*/
|
||||||
|
float vec3_length(Vec3 v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Normalize a vector
|
||||||
|
* @param v Vector to normalize
|
||||||
|
* @return Normalized vector
|
||||||
|
*/
|
||||||
|
Vec3 vec3_normalize(Vec3 v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ASCII3D_VEC3_H */
|
||||||
105
src/font.c
Normal file
105
src/font.c
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @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 "config.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 5x7 bitmap font data
|
||||||
|
* Each character is represented by 7 bytes (rows)
|
||||||
|
* Each byte contains 5 bits (columns), MSB = leftmost pixel
|
||||||
|
*/
|
||||||
|
static const unsigned char g_font_data[][FONT_HEIGHT] = {
|
||||||
|
/* A */ {0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11},
|
||||||
|
/* B */ {0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E},
|
||||||
|
/* C */ {0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E},
|
||||||
|
/* D */ {0x1C, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1C},
|
||||||
|
/* E */ {0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F},
|
||||||
|
/* F */ {0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10},
|
||||||
|
/* G */ {0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0F},
|
||||||
|
/* H */ {0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11},
|
||||||
|
/* I */ {0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E},
|
||||||
|
/* J */ {0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C},
|
||||||
|
/* K */ {0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11},
|
||||||
|
/* L */ {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F},
|
||||||
|
/* M */ {0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11},
|
||||||
|
/* N */ {0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11},
|
||||||
|
/* O */ {0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E},
|
||||||
|
/* P */ {0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10},
|
||||||
|
/* Q */ {0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D},
|
||||||
|
/* R */ {0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11},
|
||||||
|
/* S */ {0x0E, 0x11, 0x10, 0x0E, 0x01, 0x11, 0x0E},
|
||||||
|
/* T */ {0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04},
|
||||||
|
/* U */ {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E},
|
||||||
|
/* V */ {0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04},
|
||||||
|
/* W */ {0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A},
|
||||||
|
/* X */ {0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11},
|
||||||
|
/* Y */ {0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04},
|
||||||
|
/* Z */ {0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F},
|
||||||
|
/* 0 */ {0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E},
|
||||||
|
/* 1 */ {0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E},
|
||||||
|
/* 2 */ {0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F},
|
||||||
|
/* 3 */ {0x0E, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0E},
|
||||||
|
/* 4 */ {0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02},
|
||||||
|
/* 5 */ {0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E},
|
||||||
|
/* 6 */ {0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E},
|
||||||
|
/* 7 */ {0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08},
|
||||||
|
/* 8 */ {0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E},
|
||||||
|
/* 9 */ {0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FONT_LETTER_COUNT 26
|
||||||
|
#define FONT_DIGIT_COUNT 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get font index for a character
|
||||||
|
* @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';
|
||||||
|
}
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
return c - 'a';
|
||||||
|
}
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
return FONT_LETTER_COUNT + (c - '0');
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char *font_get_glyph(char c)
|
||||||
|
{
|
||||||
|
int index = font_get_index(c);
|
||||||
|
if (index < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return g_font_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool font_pixel_set(const unsigned char *glyph, int x, int y)
|
||||||
|
{
|
||||||
|
if (glyph == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (x < 0 || x >= FONT_WIDTH || y < 0 || y >= FONT_HEIGHT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* Bit 4 is leftmost (x=0), bit 0 is rightmost (x=4) */
|
||||||
|
return (glyph[y] & (1 << (FONT_WIDTH - 1 - x))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool font_is_renderable(char c)
|
||||||
|
{
|
||||||
|
return font_get_index(c) >= 0;
|
||||||
|
}
|
||||||
259
src/lighting.c
Normal file
259
src/lighting.c
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
/**
|
||||||
|
* @file lighting.c
|
||||||
|
* @brief Advanced lighting system implementation
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 2.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lighting.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void lighting_init(LightingSystem *system)
|
||||||
|
{
|
||||||
|
if (system == NULL) return;
|
||||||
|
|
||||||
|
memset(system, 0, sizeof(LightingSystem));
|
||||||
|
|
||||||
|
system->ambient_color = color_create(1.0f, 1.0f, 1.0f);
|
||||||
|
system->ambient_intensity = AMBIENT_INTENSITY;
|
||||||
|
system->camera_position = vec3_create(0.0f, 0.0f, -CAMERA_DISTANCE);
|
||||||
|
system->light_count = 0;
|
||||||
|
|
||||||
|
/* Add default key light (main light from upper-right-front) */
|
||||||
|
Light key_light = lighting_create_directional(
|
||||||
|
vec3_create(0.5f, 0.8f, 1.0f),
|
||||||
|
color_create(1.0f, 0.98f, 0.95f),
|
||||||
|
DIFFUSE_INTENSITY
|
||||||
|
);
|
||||||
|
lighting_add_light(system, &key_light);
|
||||||
|
|
||||||
|
/* Add fill light (softer light from left) */
|
||||||
|
Light fill_light = lighting_create_directional(
|
||||||
|
vec3_create(-0.7f, 0.3f, 0.5f),
|
||||||
|
color_create(0.6f, 0.7f, 1.0f),
|
||||||
|
0.3f
|
||||||
|
);
|
||||||
|
lighting_add_light(system, &fill_light);
|
||||||
|
|
||||||
|
/* Add rim/back light for edge definition */
|
||||||
|
Light rim_light = lighting_create_directional(
|
||||||
|
vec3_create(0.0f, -0.5f, -1.0f),
|
||||||
|
color_create(1.0f, 0.9f, 0.8f),
|
||||||
|
0.2f
|
||||||
|
);
|
||||||
|
lighting_add_light(system, &rim_light);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lighting_add_light(LightingSystem *system, const Light *light)
|
||||||
|
{
|
||||||
|
if (system == NULL || light == NULL) return -1;
|
||||||
|
if (system->light_count >= MAX_LIGHTS) return -1;
|
||||||
|
|
||||||
|
system->lights[system->light_count] = *light;
|
||||||
|
return system->light_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Light lighting_create_directional(Vec3 direction, Color color, float intensity)
|
||||||
|
{
|
||||||
|
Light light;
|
||||||
|
memset(&light, 0, sizeof(Light));
|
||||||
|
|
||||||
|
light.type = LIGHT_DIRECTIONAL;
|
||||||
|
light.direction = vec3_normalize(direction);
|
||||||
|
light.color = color;
|
||||||
|
light.intensity = intensity;
|
||||||
|
light.enabled = true;
|
||||||
|
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
|
||||||
|
Light lighting_create_point(Vec3 position, Color color, float intensity, float falloff)
|
||||||
|
{
|
||||||
|
Light light;
|
||||||
|
memset(&light, 0, sizeof(Light));
|
||||||
|
|
||||||
|
light.type = LIGHT_POINT;
|
||||||
|
light.position = position;
|
||||||
|
light.color = color;
|
||||||
|
light.intensity = intensity;
|
||||||
|
light.falloff = falloff;
|
||||||
|
light.enabled = true;
|
||||||
|
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate diffuse lighting contribution
|
||||||
|
*/
|
||||||
|
static float calculate_diffuse(Vec3 light_dir, Vec3 normal)
|
||||||
|
{
|
||||||
|
float ndotl = vec3_dot(normal, light_dir);
|
||||||
|
return fmaxf(0.0f, ndotl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate specular lighting contribution (Blinn-Phong)
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
float ndoth = vec3_dot(normal, half_vec);
|
||||||
|
|
||||||
|
if (ndoth <= 0.0f) return 0.0f;
|
||||||
|
|
||||||
|
return powf(ndoth, shininess);
|
||||||
|
}
|
||||||
|
|
||||||
|
float lighting_calculate(const LightingSystem *system, Vec3 point,
|
||||||
|
Vec3 normal, const Material *material)
|
||||||
|
{
|
||||||
|
if (system == NULL || material == NULL) return 0.5f;
|
||||||
|
|
||||||
|
/* Start with ambient */
|
||||||
|
float total = system->ambient_intensity;
|
||||||
|
|
||||||
|
/* View direction (from point to camera) */
|
||||||
|
Vec3 view_dir = vec3_normalize(vec3_sub(system->camera_position, point));
|
||||||
|
|
||||||
|
/* Accumulate contribution from each light */
|
||||||
|
for (int i = 0; i < system->light_count; i++) {
|
||||||
|
const Light *light = &system->lights[i];
|
||||||
|
if (!light->enabled) continue;
|
||||||
|
|
||||||
|
Vec3 light_dir;
|
||||||
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
|
if (light->type == LIGHT_DIRECTIONAL) {
|
||||||
|
/* Directional light - direction is constant */
|
||||||
|
light_dir = light->direction;
|
||||||
|
} else if (light->type == LIGHT_POINT) {
|
||||||
|
/* Point light - calculate direction and attenuation */
|
||||||
|
Vec3 to_light = vec3_sub(light->position, point);
|
||||||
|
float dist = vec3_length(to_light);
|
||||||
|
light_dir = vec3_scale(to_light, 1.0f / dist);
|
||||||
|
attenuation = 1.0f / (1.0f + light->falloff * dist * dist);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Diffuse */
|
||||||
|
float diffuse = calculate_diffuse(light_dir, normal);
|
||||||
|
|
||||||
|
/* Specular */
|
||||||
|
float specular = calculate_specular(light_dir, normal, view_dir, material->shininess);
|
||||||
|
|
||||||
|
/* Combine */
|
||||||
|
float contribution = (diffuse * DIFFUSE_INTENSITY +
|
||||||
|
specular * SPECULAR_INTENSITY) *
|
||||||
|
light->intensity * attenuation;
|
||||||
|
|
||||||
|
total += contribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clamp to reasonable range */
|
||||||
|
return fminf(1.0f, fmaxf(0.0f, total));
|
||||||
|
}
|
||||||
|
|
||||||
|
Color lighting_calculate_color(const LightingSystem *system, Vec3 point,
|
||||||
|
Vec3 normal, const Material *material)
|
||||||
|
{
|
||||||
|
if (system == NULL || material == NULL) {
|
||||||
|
return color_create(0.5f, 0.5f, 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start with ambient */
|
||||||
|
Color result = color_scale(
|
||||||
|
color_multiply(system->ambient_color, material->ambient),
|
||||||
|
system->ambient_intensity
|
||||||
|
);
|
||||||
|
|
||||||
|
/* View direction */
|
||||||
|
Vec3 view_dir = vec3_normalize(vec3_sub(system->camera_position, point));
|
||||||
|
|
||||||
|
/* Accumulate from each light */
|
||||||
|
for (int i = 0; i < system->light_count; i++) {
|
||||||
|
const Light *light = &system->lights[i];
|
||||||
|
if (!light->enabled) continue;
|
||||||
|
|
||||||
|
Vec3 light_dir;
|
||||||
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
|
if (light->type == LIGHT_DIRECTIONAL) {
|
||||||
|
light_dir = light->direction;
|
||||||
|
} else if (light->type == LIGHT_POINT) {
|
||||||
|
Vec3 to_light = vec3_sub(light->position, point);
|
||||||
|
float dist = vec3_length(to_light);
|
||||||
|
light_dir = vec3_scale(to_light, 1.0f / dist);
|
||||||
|
attenuation = 1.0f / (1.0f + light->falloff * dist * dist);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Diffuse */
|
||||||
|
float diff = calculate_diffuse(light_dir, normal);
|
||||||
|
Color diffuse = color_scale(
|
||||||
|
color_multiply(light->color, material->diffuse),
|
||||||
|
diff * light->intensity * attenuation
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Specular */
|
||||||
|
float spec = calculate_specular(light_dir, normal, view_dir, material->shininess);
|
||||||
|
Color specular = color_scale(
|
||||||
|
color_multiply(light->color, material->specular),
|
||||||
|
spec * SPECULAR_INTENSITY * light->intensity * attenuation
|
||||||
|
);
|
||||||
|
|
||||||
|
result = color_add(result, color_add(diffuse, specular));
|
||||||
|
}
|
||||||
|
|
||||||
|
return color_clamp(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Material lighting_default_material(void)
|
||||||
|
{
|
||||||
|
Material mat;
|
||||||
|
mat.ambient = color_create(0.2f, 0.2f, 0.2f);
|
||||||
|
mat.diffuse = color_create(0.8f, 0.8f, 0.8f);
|
||||||
|
mat.specular = color_create(1.0f, 1.0f, 1.0f);
|
||||||
|
mat.shininess = SPECULAR_POWER;
|
||||||
|
mat.reflectivity = 0.0f;
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color_create(float r, float g, float b)
|
||||||
|
{
|
||||||
|
Color c = {r, g, b};
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color_scale(Color c, float s)
|
||||||
|
{
|
||||||
|
return color_create(c.r * s, c.g * s, c.b * s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color_add(Color a, Color b)
|
||||||
|
{
|
||||||
|
return color_create(a.r + b.r, a.g + b.g, a.b + b.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color_multiply(Color a, Color b)
|
||||||
|
{
|
||||||
|
return color_create(a.r * b.r, a.g * b.g, a.b * b.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color_clamp(Color c)
|
||||||
|
{
|
||||||
|
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.b))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float color_to_intensity(Color c)
|
||||||
|
{
|
||||||
|
/* Luminance formula (perceptual weights) */
|
||||||
|
return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
|
||||||
|
}
|
||||||
330
src/main.c
Normal file
330
src/main.c
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "renderer.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* Global state */
|
||||||
|
static volatile sig_atomic_t g_running = 1;
|
||||||
|
static RenderSettings g_settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal handler for graceful shutdown
|
||||||
|
*/
|
||||||
|
static void signal_handler(int sig)
|
||||||
|
{
|
||||||
|
(void)sig;
|
||||||
|
g_running = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup signal handlers
|
||||||
|
*/
|
||||||
|
static void setup_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = signal_handler;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
|
||||||
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
sigaction(SIGTERM, &sa, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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("Usage: %s [OPTIONS] [TEXT]\n\n", program_name);
|
||||||
|
|
||||||
|
printf("ROTATION OPTIONS:\n");
|
||||||
|
printf(" -s <speed> Rotation speed multiplier (default: 1.0)\n");
|
||||||
|
printf(" -x Enable X-axis rotation\n");
|
||||||
|
printf(" -y Enable Y-axis rotation (default)\n");
|
||||||
|
printf(" -z Enable Z-axis rotation\n");
|
||||||
|
printf(" -a Enable all axis rotations\n");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("RENDER MODE OPTIONS:\n");
|
||||||
|
printf(" -m <mode> Render mode:\n");
|
||||||
|
printf(" 0 = Solid (filled)\n");
|
||||||
|
printf(" 1 = Wireframe (edges only)\n");
|
||||||
|
printf(" 2 = Points (sparse)\n");
|
||||||
|
printf(" 3 = Shaded (full Phong lighting) [default]\n");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("COLOR OPTIONS:\n");
|
||||||
|
printf(" -c <mode> Color mode:\n");
|
||||||
|
printf(" 0 = Monochrome ASCII [default]\n");
|
||||||
|
printf(" 1 = 16-color ANSI\n");
|
||||||
|
printf(" 2 = 256-color ANSI\n");
|
||||||
|
printf(" 3 = Truecolor (24-bit RGB)\n");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("QUALITY OPTIONS:\n");
|
||||||
|
printf(" -q <quality> Render quality (0.5 - 2.0, default: 1.0)\n");
|
||||||
|
printf(" -A Enable anti-aliasing\n");
|
||||||
|
printf(" -p <palette> Shading palette:\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(" -f Show FPS counter\n");
|
||||||
|
printf(" -h Show this help message\n");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("EXAMPLES:\n");
|
||||||
|
printf(" %s HELLO Simple Y-axis rotation\n", program_name);
|
||||||
|
printf(" %s -a -c3 WORLD Truecolor tumbling\n", program_name);
|
||||||
|
printf(" %s -m1 -s2 WIRE Fast wireframe\n", program_name);
|
||||||
|
printf(" %s -A -q1.5 -c2 HQ High quality with AA\n", program_name);
|
||||||
|
printf(" %s -p2 BLOCKS Block character style\n", program_name);
|
||||||
|
printf("\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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse command line arguments
|
||||||
|
*/
|
||||||
|
static bool parse_arguments(int argc, char *argv[],
|
||||||
|
RotationState *rotation, const char **text)
|
||||||
|
{
|
||||||
|
*rotation = renderer_default_rotation();
|
||||||
|
g_settings = renderer_default_settings();
|
||||||
|
*text = "3D";
|
||||||
|
|
||||||
|
bool explicit_rotation = false;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (argv[i][0] == '-') {
|
||||||
|
const char *opt = &argv[i][1];
|
||||||
|
|
||||||
|
while (*opt) {
|
||||||
|
switch (*opt) {
|
||||||
|
case 's':
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
rotation->speed = (float)atof(argv[++i]);
|
||||||
|
if (rotation->speed <= 0.0f) rotation->speed = 1.0f;
|
||||||
|
}
|
||||||
|
goto next_arg;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
rotation->enable_x = true;
|
||||||
|
explicit_rotation = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'y':
|
||||||
|
rotation->enable_y = true;
|
||||||
|
explicit_rotation = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'z':
|
||||||
|
rotation->enable_z = true;
|
||||||
|
explicit_rotation = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
rotation->enable_x = true;
|
||||||
|
rotation->enable_y = true;
|
||||||
|
rotation->enable_z = true;
|
||||||
|
explicit_rotation = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
int mode = atoi(argv[++i]);
|
||||||
|
if (mode >= 0 && mode < RENDER_MODE_COUNT) {
|
||||||
|
g_settings.mode = (RenderMode)mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto next_arg;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
int cmode = atoi(argv[++i]);
|
||||||
|
if (cmode >= 0 && cmode < COLOR_MODE_COUNT) {
|
||||||
|
g_settings.color_mode = (ColorMode)cmode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto next_arg;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
g_settings.quality = (float)atof(argv[++i]);
|
||||||
|
if (g_settings.quality < 0.5f) g_settings.quality = 0.5f;
|
||||||
|
if (g_settings.quality > 2.0f) g_settings.quality = 2.0f;
|
||||||
|
}
|
||||||
|
goto next_arg;
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
g_settings.anti_aliasing = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
int pal = atoi(argv[++i]);
|
||||||
|
if (pal >= 0 && pal < 4) {
|
||||||
|
g_settings.palette_index = pal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto next_arg;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
g_settings.show_fps = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown option: -%c\n", *opt);
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
opt++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*text = argv[i];
|
||||||
|
}
|
||||||
|
next_arg:;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle rotation defaults */
|
||||||
|
if (explicit_rotation && !rotation->enable_y) {
|
||||||
|
/* User explicitly chose axes */
|
||||||
|
} else if (!explicit_rotation) {
|
||||||
|
rotation->enable_y = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main render loop with advanced features
|
||||||
|
*/
|
||||||
|
static void render_loop(const char *text, RotationState *rotation)
|
||||||
|
{
|
||||||
|
double last_time = timing_get_seconds();
|
||||||
|
double fps_update_time = last_time;
|
||||||
|
int frame_count = 0;
|
||||||
|
double current_fps = 0.0;
|
||||||
|
|
||||||
|
/* Apply settings */
|
||||||
|
renderer_set_mode(g_settings.mode);
|
||||||
|
renderer_set_color_mode(g_settings.color_mode);
|
||||||
|
renderer_set_palette(g_settings.palette_index);
|
||||||
|
|
||||||
|
while (g_running) {
|
||||||
|
double current_time = timing_get_seconds();
|
||||||
|
double delta_time = current_time - last_time;
|
||||||
|
last_time = current_time;
|
||||||
|
|
||||||
|
/* Update FPS counter */
|
||||||
|
frame_count++;
|
||||||
|
if (current_time - fps_update_time >= 1.0) {
|
||||||
|
current_fps = (double)frame_count / (current_time - fps_update_time);
|
||||||
|
frame_count = 0;
|
||||||
|
fps_update_time = current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update rotation */
|
||||||
|
renderer_update_rotation(rotation, delta_time);
|
||||||
|
|
||||||
|
/* Render frame */
|
||||||
|
renderer_clear();
|
||||||
|
renderer_draw_text_ex(text, rotation, &g_settings);
|
||||||
|
|
||||||
|
/* Present with appropriate color mode */
|
||||||
|
if (g_settings.color_mode != COLOR_MODE_MONO) {
|
||||||
|
renderer_present_color(&g_settings);
|
||||||
|
} else {
|
||||||
|
renderer_present();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show FPS if enabled */
|
||||||
|
if (g_settings.show_fps) {
|
||||||
|
printf("\033[1;1H\033[7m FPS: %.1f | Voxels: %d \033[0m",
|
||||||
|
current_fps, renderer_get_stats().triangles);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit frame rate */
|
||||||
|
timing_limit_fps(current_time, TARGET_FPS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Program entry point
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
RotationState rotation;
|
||||||
|
const char *text;
|
||||||
|
|
||||||
|
/* Parse command line */
|
||||||
|
if (!parse_arguments(argc, argv, &rotation, &text)) {
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup signal handlers */
|
||||||
|
setup_signals();
|
||||||
|
|
||||||
|
/* Initialize renderer */
|
||||||
|
if (renderer_init() != 0) {
|
||||||
|
fprintf(stderr, "Failed to initialize renderer\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter alternate screen buffer for clean exit */
|
||||||
|
renderer_enter_alternate_screen();
|
||||||
|
renderer_clear_terminal();
|
||||||
|
renderer_hide_cursor();
|
||||||
|
|
||||||
|
/* Run main loop */
|
||||||
|
render_loop(text, &rotation);
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
renderer_show_cursor();
|
||||||
|
renderer_exit_alternate_screen();
|
||||||
|
renderer_cleanup();
|
||||||
|
|
||||||
|
printf("Goodbye!\n");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
677
src/renderer.c
Normal file
677
src/renderer.c
Normal file
@@ -0,0 +1,677 @@
|
|||||||
|
/**
|
||||||
|
* @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 "vec3.h"
|
||||||
|
#include "font.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* GLOBAL STATE
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* Screen buffer - character display */
|
||||||
|
static char g_screen[SCREEN_HEIGHT][SCREEN_WIDTH + 1];
|
||||||
|
|
||||||
|
/* Depth buffer for z-testing */
|
||||||
|
static float g_zbuffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
|
/* Normal buffer for post-processing effects */
|
||||||
|
static Vec3 g_normal_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
|
/* Color buffer for colored output */
|
||||||
|
static Color g_color_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
|
/* Intensity accumulator for anti-aliasing */
|
||||||
|
static float g_intensity_buffer[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
static int g_sample_count[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||||
|
|
||||||
|
/* Lighting system */
|
||||||
|
static LightingSystem g_lighting;
|
||||||
|
|
||||||
|
/* Current render settings */
|
||||||
|
static RenderSettings g_settings;
|
||||||
|
|
||||||
|
/* Current render mode */
|
||||||
|
static RenderMode g_render_mode = RENDER_MODE_SHADED;
|
||||||
|
|
||||||
|
/* Current color mode */
|
||||||
|
static ColorMode g_color_mode = COLOR_MODE_MONO;
|
||||||
|
|
||||||
|
/* Frame statistics */
|
||||||
|
static FrameStats g_stats;
|
||||||
|
|
||||||
|
/* Shading palettes */
|
||||||
|
static const char *g_palettes[] = {
|
||||||
|
SHADE_CHARS_STANDARD,
|
||||||
|
SHADE_CHARS_EXTENDED,
|
||||||
|
SHADE_CHARS_BLOCK,
|
||||||
|
SHADE_CHARS_MINIMAL
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Voxel counter for stats */
|
||||||
|
static int g_voxel_count;
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* PROJECTION
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Project a 3D point to 2D screen coordinates with perspective
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Camera space transformation */
|
||||||
|
float z = point.z + CAMERA_DISTANCE;
|
||||||
|
|
||||||
|
/* Near plane clipping */
|
||||||
|
if (z < NEAR_PLANE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Far plane clipping */
|
||||||
|
if (z > FAR_PLANE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perspective division */
|
||||||
|
float scale = FIELD_OF_VIEW / z;
|
||||||
|
*out_x = point.x * scale * aspect + (float)SCREEN_WIDTH / 2.0f;
|
||||||
|
*out_y = -point.y * scale + (float)SCREEN_HEIGHT / 2.0f;
|
||||||
|
*out_z = z;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* PIXEL PLOTTING
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 iy = (int)sy;
|
||||||
|
|
||||||
|
/* Bounds check */
|
||||||
|
if (ix < 0 || ix >= SCREEN_WIDTH || iy < 0 || iy >= SCREEN_HEIGHT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Depth test */
|
||||||
|
if (sz >= g_zbuffer[iy][ix]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_zbuffer[iy][ix] = sz;
|
||||||
|
g_normal_buffer[iy][ix] = normal;
|
||||||
|
g_voxel_count++;
|
||||||
|
|
||||||
|
/* Calculate lighting */
|
||||||
|
float intensity;
|
||||||
|
Color color;
|
||||||
|
|
||||||
|
if (g_render_mode == RENDER_MODE_SHADED) {
|
||||||
|
/* Full Phong lighting */
|
||||||
|
color = lighting_calculate_color(&g_lighting, point, normal, &g_material);
|
||||||
|
intensity = color_to_intensity(color);
|
||||||
|
} else if (g_render_mode == RENDER_MODE_SOLID) {
|
||||||
|
/* Simple normal-based shading */
|
||||||
|
intensity = (normal.z + 1.0f) * 0.5f;
|
||||||
|
intensity = fmaxf(0.0f, fminf(1.0f, intensity));
|
||||||
|
color = color_scale(g_settings.base_color, intensity);
|
||||||
|
} else {
|
||||||
|
/* Wireframe/points - full brightness */
|
||||||
|
intensity = 1.0f;
|
||||||
|
color = g_settings.base_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_color_buffer[iy][ix] = color;
|
||||||
|
g_intensity_buffer[iy][ix] = intensity;
|
||||||
|
g_sample_count[iy][ix] = 1;
|
||||||
|
|
||||||
|
/* Convert intensity to ASCII character */
|
||||||
|
const char *palette = g_palettes[g_current_palette];
|
||||||
|
int palette_size = g_palette_sizes[g_current_palette];
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
g_screen[iy][ix] = palette[shade_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Plot with anti-aliasing (accumulate samples)
|
||||||
|
*/
|
||||||
|
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 iy = (int)sy;
|
||||||
|
|
||||||
|
if (ix < 0 || ix >= SCREEN_WIDTH || iy < 0 || iy >= SCREEN_HEIGHT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For AA, we accumulate and average */
|
||||||
|
if (sz < g_zbuffer[iy][ix]) {
|
||||||
|
g_zbuffer[iy][ix] = sz;
|
||||||
|
g_normal_buffer[iy][ix] = normal;
|
||||||
|
|
||||||
|
Color color = lighting_calculate_color(&g_lighting, point, normal, &g_material);
|
||||||
|
float intensity = color_to_intensity(color);
|
||||||
|
|
||||||
|
g_intensity_buffer[iy][ix] += intensity;
|
||||||
|
g_color_buffer[iy][ix] = color_add(g_color_buffer[iy][ix], color);
|
||||||
|
g_sample_count[iy][ix]++;
|
||||||
|
g_voxel_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* EDGE DETECTION FOR SURFACE RENDERING
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
static bool neighbor_empty(const unsigned char *glyph, int x, int y, int dx, int dy)
|
||||||
|
{
|
||||||
|
int nx = x + dx;
|
||||||
|
int ny = y + dy;
|
||||||
|
|
||||||
|
if (nx < 0 || nx >= FONT_WIDTH || ny < 0 || ny >= FONT_HEIGHT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !font_pixel_set(glyph, nx, ny);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate smooth normal by averaging adjacent face normals
|
||||||
|
*/
|
||||||
|
static Vec3 calculate_smooth_normal(const unsigned char *glyph, int gx, int gy,
|
||||||
|
float gz, bool is_front, bool is_back)
|
||||||
|
{
|
||||||
|
Vec3 normal = vec3_create(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
/* Front/back face contribution */
|
||||||
|
if (is_front) {
|
||||||
|
normal = vec3_add(normal, vec3_create(0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
if (is_back) {
|
||||||
|
normal = vec3_add(normal, vec3_create(0.0f, 0.0f, -1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Side face contributions */
|
||||||
|
if (neighbor_empty(glyph, gx, gy, -1, 0)) {
|
||||||
|
normal = vec3_add(normal, vec3_create(-1.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
if (neighbor_empty(glyph, gx, gy, 1, 0)) {
|
||||||
|
normal = vec3_add(normal, vec3_create(1.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
if (neighbor_empty(glyph, gx, gy, 0, -1)) {
|
||||||
|
normal = vec3_add(normal, vec3_create(0.0f, 1.0f, 0.0f));
|
||||||
|
}
|
||||||
|
if (neighbor_empty(glyph, gx, gy, 0, 1)) {
|
||||||
|
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)) {
|
||||||
|
normal = vec3_add(normal, vec3_scale(vec3_create(-0.707f, 0.707f, 0.0f), 0.5f));
|
||||||
|
}
|
||||||
|
if (neighbor_empty(glyph, gx, gy, 1, -1)) {
|
||||||
|
normal = vec3_add(normal, vec3_scale(vec3_create(0.707f, 0.707f, 0.0f), 0.5f));
|
||||||
|
}
|
||||||
|
if (neighbor_empty(glyph, gx, gy, -1, 1)) {
|
||||||
|
normal = vec3_add(normal, vec3_scale(vec3_create(-0.707f, -0.707f, 0.0f), 0.5f));
|
||||||
|
}
|
||||||
|
if (neighbor_empty(glyph, gx, gy, 1, 1)) {
|
||||||
|
normal = vec3_add(normal, vec3_scale(vec3_create(0.707f, -0.707f, 0.0f), 0.5f));
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)gz; /* Suppress unused warning */
|
||||||
|
|
||||||
|
return vec3_normalize(normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* CHARACTER RENDERING
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
if (glyph == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center offsets */
|
||||||
|
float cx = (float)FONT_WIDTH / 2.0f;
|
||||||
|
float cy = (float)FONT_HEIGHT / 2.0f;
|
||||||
|
float cz = EXTRUSION_DEPTH / 2.0f;
|
||||||
|
|
||||||
|
/* Quality-adjusted voxel step */
|
||||||
|
float step = VOXEL_STEP / settings->quality;
|
||||||
|
|
||||||
|
/* Iterate through each voxel */
|
||||||
|
for (int gy = 0; gy < FONT_HEIGHT; gy++) {
|
||||||
|
for (int gx = 0; gx < FONT_WIDTH; gx++) {
|
||||||
|
if (!font_pixel_set(glyph, gx, gy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Render along Z depth (extrusion) */
|
||||||
|
for (float gz = 0.0f; gz <= EXTRUSION_DEPTH; gz += step) {
|
||||||
|
/* Determine if this is a surface voxel */
|
||||||
|
bool is_front = (gz < step);
|
||||||
|
bool is_back = (gz > EXTRUSION_DEPTH - step);
|
||||||
|
bool left_empty = neighbor_empty(glyph, gx, gy, -1, 0);
|
||||||
|
bool right_empty = neighbor_empty(glyph, gx, gy, 1, 0);
|
||||||
|
bool top_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 ||
|
||||||
|
left_empty || right_empty ||
|
||||||
|
top_empty || bottom_empty;
|
||||||
|
|
||||||
|
/* Wireframe mode: only render edges */
|
||||||
|
if (g_render_mode == RENDER_MODE_WIREFRAME) {
|
||||||
|
int edge_count = (is_front ? 1 : 0) + (is_back ? 1 : 0) +
|
||||||
|
(left_empty ? 1 : 0) + (right_empty ? 1 : 0) +
|
||||||
|
(top_empty ? 1 : 0) + (bottom_empty ? 1 : 0);
|
||||||
|
if (edge_count < 2) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create 3D point centered at origin */
|
||||||
|
Vec3 point = vec3_create(
|
||||||
|
((float)gx - cx) * CHAR_SCALE + offset_x,
|
||||||
|
(cy - (float)gy) * CHAR_SCALE,
|
||||||
|
(gz - cz) * CHAR_SCALE
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Calculate smooth normal for better shading */
|
||||||
|
Vec3 normal = calculate_smooth_normal(glyph, gx, gy, gz, is_front, is_back);
|
||||||
|
|
||||||
|
/* Apply rotations */
|
||||||
|
if (rotation->enable_x) {
|
||||||
|
point = vec3_rotate_x(point, rotation->angle_x);
|
||||||
|
normal = vec3_rotate_x(normal, rotation->angle_x);
|
||||||
|
}
|
||||||
|
if (rotation->enable_y) {
|
||||||
|
point = vec3_rotate_y(point, rotation->angle_y);
|
||||||
|
normal = vec3_rotate_y(normal, rotation->angle_y);
|
||||||
|
}
|
||||||
|
if (rotation->enable_z) {
|
||||||
|
point = vec3_rotate_z(point, rotation->angle_z);
|
||||||
|
normal = vec3_rotate_z(normal, rotation->angle_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Project and render */
|
||||||
|
float sx, sy, sz;
|
||||||
|
if (project_point(point, &sx, &sy, &sz)) {
|
||||||
|
if (settings->anti_aliasing) {
|
||||||
|
/* Multi-sample AA */
|
||||||
|
for (int aay = 0; aay < AA_SAMPLES; aay++) {
|
||||||
|
for (int aax = 0; aax < AA_SAMPLES; aax++) {
|
||||||
|
float ox = (float)aax / (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
plot_point_lit(sx, sy, sz, point, normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
* PUBLIC API
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
int renderer_init(void)
|
||||||
|
{
|
||||||
|
/* Initialize lighting system */
|
||||||
|
lighting_init(&g_lighting);
|
||||||
|
|
||||||
|
/* Initialize default material */
|
||||||
|
g_material = lighting_default_material();
|
||||||
|
g_material.diffuse = color_create(0.9f, 0.9f, 0.95f);
|
||||||
|
g_material.specular = color_create(1.0f, 1.0f, 1.0f);
|
||||||
|
g_material.shininess = 64.0f;
|
||||||
|
|
||||||
|
/* Initialize settings */
|
||||||
|
g_settings = renderer_default_settings();
|
||||||
|
|
||||||
|
/* Clear buffers */
|
||||||
|
renderer_clear();
|
||||||
|
|
||||||
|
/* Initialize stats */
|
||||||
|
memset(&g_stats, 0, sizeof(g_stats));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_cleanup(void)
|
||||||
|
{
|
||||||
|
/* Nothing to clean up */
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_clear(void)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
|
memset(g_screen[y], ' ', SCREEN_WIDTH);
|
||||||
|
g_screen[y][SCREEN_WIDTH] = '\0';
|
||||||
|
|
||||||
|
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
|
g_zbuffer[y][x] = DEPTH_BUFFER_INIT;
|
||||||
|
g_normal_buffer[y][x] = vec3_create(0.0f, 0.0f, 0.0f);
|
||||||
|
g_color_buffer[y][x] = color_create(0.0f, 0.0f, 0.0f);
|
||||||
|
g_intensity_buffer[y][x] = 0.0f;
|
||||||
|
g_sample_count[y][x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_voxel_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_draw_text(const char *text, const RotationState *rotation)
|
||||||
|
{
|
||||||
|
renderer_draw_text_ex(text, rotation, &g_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_draw_text_ex(const char *text, const RotationState *rotation,
|
||||||
|
const RenderSettings *settings)
|
||||||
|
{
|
||||||
|
if (text == NULL || rotation == NULL || settings == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(text);
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate total width for centering */
|
||||||
|
float char_width = (float)(FONT_WIDTH + FONT_CHAR_SPACING) * CHAR_SCALE;
|
||||||
|
float total_width = (float)len * char_width;
|
||||||
|
float start_x = -total_width / 2.0f + char_width / 2.0f;
|
||||||
|
|
||||||
|
/* Render each character */
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
if (text[i] == ' ') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float offset_x = start_x + (float)i * char_width;
|
||||||
|
render_char_advanced(text[i], offset_x, rotation, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize AA samples */
|
||||||
|
if (settings->anti_aliasing) {
|
||||||
|
const char *palette = g_palettes[g_current_palette];
|
||||||
|
int palette_size = g_palette_sizes[g_current_palette];
|
||||||
|
|
||||||
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
|
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
|
if (g_sample_count[y][x] > 0) {
|
||||||
|
float avg_intensity = g_intensity_buffer[y][x] / (float)g_sample_count[y][x];
|
||||||
|
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_stats.triangles = g_voxel_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_present(void)
|
||||||
|
{
|
||||||
|
/* Move cursor to home position */
|
||||||
|
printf("\033[H");
|
||||||
|
|
||||||
|
/* Output screen buffer */
|
||||||
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
|
printf("%s\n", g_screen[y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_present_color(const RenderSettings *settings)
|
||||||
|
{
|
||||||
|
if (settings == NULL || settings->color_mode == COLOR_MODE_MONO) {
|
||||||
|
renderer_present();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\033[H");
|
||||||
|
|
||||||
|
for (int y = 0; y < SCREEN_HEIGHT; y++) {
|
||||||
|
for (int x = 0; x < SCREEN_WIDTH; x++) {
|
||||||
|
char c = g_screen[y][x];
|
||||||
|
|
||||||
|
if (c == ' ') {
|
||||||
|
putchar(' ');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color col = g_color_buffer[y][x];
|
||||||
|
|
||||||
|
if (settings->color_mode == COLOR_MODE_TRUECOLOR) {
|
||||||
|
/* 24-bit truecolor */
|
||||||
|
int r = (int)(col.r * 255.0f);
|
||||||
|
int g = (int)(col.g * 255.0f);
|
||||||
|
int b = (int)(col.b * 255.0f);
|
||||||
|
r = r < 0 ? 0 : (r > 255 ? 255 : r);
|
||||||
|
g = g < 0 ? 0 : (g > 255 ? 255 : g);
|
||||||
|
b = b < 0 ? 0 : (b > 255 ? 255 : b);
|
||||||
|
printf("\033[38;2;%d;%d;%dm%c", r, g, b, c);
|
||||||
|
} else if (settings->color_mode == COLOR_MODE_ANSI_256) {
|
||||||
|
/* 256-color mode - approximate */
|
||||||
|
int r = (int)(col.r * 5.0f);
|
||||||
|
int g_val = (int)(col.g * 5.0f);
|
||||||
|
int b = (int)(col.b * 5.0f);
|
||||||
|
r = r < 0 ? 0 : (r > 5 ? 5 : r);
|
||||||
|
g_val = g_val < 0 ? 0 : (g_val > 5 ? 5 : g_val);
|
||||||
|
b = b < 0 ? 0 : (b > 5 ? 5 : b);
|
||||||
|
int color_code = 16 + 36 * r + 6 * g_val + b;
|
||||||
|
printf("\033[38;5;%dm%c", color_code, c);
|
||||||
|
} else {
|
||||||
|
/* 16-color ANSI */
|
||||||
|
float intensity = color_to_intensity(col);
|
||||||
|
int bright = intensity > 0.5f ? 1 : 0;
|
||||||
|
int base = 30;
|
||||||
|
|
||||||
|
/* Simple color mapping */
|
||||||
|
if (col.r > col.g && col.r > col.b) {
|
||||||
|
base = 31; /* Red */
|
||||||
|
} else if (col.g > col.r && col.g > col.b) {
|
||||||
|
base = 32; /* Green */
|
||||||
|
} else if (col.b > col.r && col.b > col.g) {
|
||||||
|
base = 34; /* Blue */
|
||||||
|
} else if (col.r > 0.5f && col.g > 0.5f) {
|
||||||
|
base = 33; /* Yellow */
|
||||||
|
} else if (col.r > 0.5f && col.b > 0.5f) {
|
||||||
|
base = 35; /* Magenta */
|
||||||
|
} else if (col.g > 0.5f && col.b > 0.5f) {
|
||||||
|
base = 36; /* Cyan */
|
||||||
|
} else {
|
||||||
|
base = 37; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bright) {
|
||||||
|
printf("\033[1;%dm%c", base, c);
|
||||||
|
} else {
|
||||||
|
printf("\033[%dm%c", base, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(ANSI_RESET "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_update_rotation(RotationState *rotation, double delta_time)
|
||||||
|
{
|
||||||
|
if (rotation == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dt = (float)delta_time * rotation->speed;
|
||||||
|
|
||||||
|
if (rotation->enable_x) {
|
||||||
|
rotation->angle_x += dt * 0.7f;
|
||||||
|
}
|
||||||
|
if (rotation->enable_y) {
|
||||||
|
rotation->angle_y += dt * 1.0f;
|
||||||
|
}
|
||||||
|
if (rotation->enable_z) {
|
||||||
|
rotation->angle_z += dt * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep angles in reasonable range */
|
||||||
|
const float TWO_PI = 6.283185307f;
|
||||||
|
if (rotation->angle_x > TWO_PI) rotation->angle_x -= 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 state = {
|
||||||
|
.angle_x = 0.0f,
|
||||||
|
.angle_y = 0.0f,
|
||||||
|
.angle_z = 0.0f,
|
||||||
|
.enable_x = false,
|
||||||
|
.enable_y = true,
|
||||||
|
.enable_z = false,
|
||||||
|
.speed = 1.0f
|
||||||
|
};
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderSettings renderer_default_settings(void)
|
||||||
|
{
|
||||||
|
RenderSettings settings = {
|
||||||
|
.mode = RENDER_MODE_SHADED,
|
||||||
|
.color_mode = COLOR_MODE_MONO,
|
||||||
|
.anti_aliasing = false,
|
||||||
|
.show_fps = false,
|
||||||
|
.auto_rotate = true,
|
||||||
|
.quality = 1.0f,
|
||||||
|
.palette_index = 1,
|
||||||
|
.base_color = color_create(1.0f, 1.0f, 1.0f),
|
||||||
|
.highlight_color = color_create(1.0f, 1.0f, 1.0f)
|
||||||
|
};
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_set_mode(RenderMode mode)
|
||||||
|
{
|
||||||
|
if (mode < RENDER_MODE_COUNT) {
|
||||||
|
g_render_mode = mode;
|
||||||
|
g_settings.mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_set_color_mode(ColorMode mode)
|
||||||
|
{
|
||||||
|
if (mode < COLOR_MODE_COUNT) {
|
||||||
|
g_color_mode = mode;
|
||||||
|
g_settings.color_mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameStats renderer_get_stats(void)
|
||||||
|
{
|
||||||
|
return g_stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_set_palette(int palette_index)
|
||||||
|
{
|
||||||
|
if (palette_index >= 0 && palette_index < 4) {
|
||||||
|
g_current_palette = palette_index;
|
||||||
|
g_settings.palette_index = palette_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LightingSystem *renderer_get_lighting(void)
|
||||||
|
{
|
||||||
|
return &g_lighting;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_hide_cursor(void)
|
||||||
|
{
|
||||||
|
printf("\033[?25l");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_show_cursor(void)
|
||||||
|
{
|
||||||
|
printf("\033[?25h");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_clear_terminal(void)
|
||||||
|
{
|
||||||
|
printf("\033[2J");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_enter_alternate_screen(void)
|
||||||
|
{
|
||||||
|
printf("\033[?1049h");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderer_exit_alternate_screen(void)
|
||||||
|
{
|
||||||
|
printf("\033[?1049l");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
43
src/timing.c
Normal file
43
src/timing.c
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @file timing.c
|
||||||
|
* @brief High-precision timing utilities implementation
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
double timing_get_seconds(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return (double)ts.tv_sec + (double)ts.tv_nsec / 1.0e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timing_sleep_us(unsigned int microseconds)
|
||||||
|
{
|
||||||
|
struct timespec req;
|
||||||
|
req.tv_sec = microseconds / 1000000;
|
||||||
|
req.tv_nsec = (microseconds % 1000000) * 1000L;
|
||||||
|
nanosleep(&req, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timing_limit_fps(double frame_start_time, int target_fps)
|
||||||
|
{
|
||||||
|
if (target_fps <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double target_frame_time = 1.0 / (double)target_fps;
|
||||||
|
double elapsed = timing_get_seconds() - frame_start_time;
|
||||||
|
double sleep_time = target_frame_time - elapsed;
|
||||||
|
|
||||||
|
if (sleep_time > 0.0) {
|
||||||
|
unsigned int sleep_us = (unsigned int)(sleep_time * 1.0e6);
|
||||||
|
timing_sleep_us(sleep_us);
|
||||||
|
}
|
||||||
|
}
|
||||||
91
src/vec3.c
Normal file
91
src/vec3.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @file vec3.c
|
||||||
|
* @brief 3D Vector mathematics implementation
|
||||||
|
* @author ASCII3D Project
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vec3.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
Vec3 vec3_create(float x, float y, float z)
|
||||||
|
{
|
||||||
|
Vec3 v = {x, y, z};
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vec3_add(Vec3 a, Vec3 b)
|
||||||
|
{
|
||||||
|
return vec3_create(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vec3_sub(Vec3 a, Vec3 b)
|
||||||
|
{
|
||||||
|
return vec3_create(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vec3_scale(Vec3 v, float s)
|
||||||
|
{
|
||||||
|
return vec3_create(v.x * s, v.y * s, v.z * s);
|
||||||
|
}
|
||||||
|
|
||||||
|
float vec3_dot(Vec3 a, Vec3 b)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float vec3_length(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);
|
||||||
|
if (len > 0.0001f) {
|
||||||
|
return vec3_scale(v, 1.0f / len);
|
||||||
|
}
|
||||||
|
return vec3_create(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vec3_rotate_x(Vec3 v, float angle)
|
||||||
|
{
|
||||||
|
float c = cosf(angle);
|
||||||
|
float s = sinf(angle);
|
||||||
|
return vec3_create(
|
||||||
|
v.x,
|
||||||
|
v.y * c - v.z * s,
|
||||||
|
v.y * s + v.z * c
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vec3_rotate_y(Vec3 v, float angle)
|
||||||
|
{
|
||||||
|
float c = cosf(angle);
|
||||||
|
float s = sinf(angle);
|
||||||
|
return vec3_create(
|
||||||
|
v.x * c + v.z * s,
|
||||||
|
v.y,
|
||||||
|
-v.x * s + v.z * c
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vec3_rotate_z(Vec3 v, float angle)
|
||||||
|
{
|
||||||
|
float c = cosf(angle);
|
||||||
|
float s = sinf(angle);
|
||||||
|
return vec3_create(
|
||||||
|
v.x * c - v.y * s,
|
||||||
|
v.x * s + v.y * c,
|
||||||
|
v.z
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user