Refactor tilemap examples and enhance biome generation
- Removed dual_noise_demo example as it was deemed unnecessary. - Added perlin_demo example for visualizing Perlin noise. - Updated biome_demo to generate SVG visualizations of tilemaps. - Changed biome properties from thresholds to ratios for better control. - Modified terrain generation logic to accommodate new biome properties. - Improved documentation with detailed API and usage examples. Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
parent
1289e99fc3
commit
1cb4c19b77
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@ -1,2 +1,2 @@
|
||||
+ Always use English for code comments.
|
||||
+ Always use English for code comments and documents.
|
||||
+ Don't create getters or setters if they are not necessary.
|
||||
|
203
README.md
203
README.md
@ -1,205 +1,4 @@
|
||||
# Instructed Project
|
||||
|
||||
一个使用现代C++实现的地图生成系统项目。
|
||||
A MMO RTS programming game that involves writing code to control units, build structures, manage resources, and fight with other players in a multiplayer online environment.
|
||||
|
||||
## 项目组成
|
||||
|
||||
### 🗺️ Tilemap Library
|
||||
位于 `tilemap/` 目录下的核心地图生成库,提供:
|
||||
|
||||
- **Perlin噪声地形生成**: 基于噪声算法的自然地形
|
||||
- **生物群系系统**: 9种基于气候的生物群系
|
||||
- **高效区块系统**: 支持大规模地图生成
|
||||
- **现代C++设计**: 使用C++23标准
|
||||
|
||||
### 📁 项目结构
|
||||
|
||||
```
|
||||
instructed/
|
||||
├── tilemap/ # Tilemap库
|
||||
│ ├── include/ # 头文件
|
||||
│ ├── src/ # 库源代码
|
||||
│ ├── examples/ # 示例程序
|
||||
│ └── README.md # 库文档
|
||||
├── CMakeLists.txt # 主构建文件
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 构建项目
|
||||
|
||||
```bash
|
||||
# 克隆或下载项目
|
||||
cd instructed
|
||||
|
||||
# 构建
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
|
||||
# 运行示例
|
||||
./build/tilemap/examples/basic_demo
|
||||
```
|
||||
|
||||
### 禁用示例程序构建
|
||||
|
||||
```bash
|
||||
cmake -DBUILD_EXAMPLES=OFF ..
|
||||
make
|
||||
```
|
||||
|
||||
## 🎮 示例程序
|
||||
|
||||
| 程序 | 描述 |
|
||||
|------|------|
|
||||
| `basic_demo` | 基础地图生成演示 |
|
||||
| `advanced_demo` | 高级功能和统计信息 |
|
||||
| `biome_demo` | 生物群系系统演示 |
|
||||
| `advanced_biome_demo` | 生物群系分析和可视化 |
|
||||
|
||||
## 📖 文档
|
||||
|
||||
- [Tilemap库使用指南](tilemap/README.md)
|
||||
- [生物群系系统详解](BIOME_SYSTEM_GUIDE.md)
|
||||
- [传统使用方法](TILEMAP_USAGE.md)
|
||||
|
||||
## 🛠️ 技术要求
|
||||
|
||||
- **C++23**: 现代C++特性支持
|
||||
- **CMake 3.27+**: 构建系统
|
||||
- **支持的编译器**: GCC 13+, Clang 16+, MSVC 2022+
|
||||
|
||||
## 📝 开发说明
|
||||
|
||||
这个项目展示了:
|
||||
- 清晰的库和示例分离
|
||||
- 现代CMake最佳实践
|
||||
- 模块化的C++库设计
|
||||
- 完整的文档和示例
|
||||
|
||||
## Features
|
||||
|
||||
- **Chunk-based Architecture**: Divides the world into 64x64 tile chunks for efficient memory management
|
||||
- **Multiple Tile Types**: Supports different terrain types (Empty, Grass, Stone, Water, Sand, Forest)
|
||||
- **Perlin Noise Generation**: Uses Perlin noise algorithm for natural-looking terrain generation
|
||||
- **Flexible Map Size**: Support for n×n chunks (configurable map dimensions)
|
||||
- **Efficient Access**: Fast tile and chunk access with coordinate conversion utilities
|
||||
- **Tile Properties**: Tiles have properties like walkability and liquid state
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
include/
|
||||
├── tile.h # Individual tile class
|
||||
├── chunk.h # 64x64 chunk of tiles
|
||||
├── tilemap.h # Main tilemap manager
|
||||
└── random.h # Random number and noise generation
|
||||
|
||||
src/
|
||||
├── tile.cpp
|
||||
├── chunk.cpp
|
||||
├── tilemap.cpp
|
||||
├── random.cpp
|
||||
└── main.cpp # Demo application
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
### Option 1: Using Make
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
### Option 2: Using CMake
|
||||
```bash
|
||||
make cmake
|
||||
```
|
||||
|
||||
### Option 3: Manual compilation
|
||||
```bash
|
||||
g++ -std=c++17 -Wall -Wextra -O2 -Iinclude src/*.cpp -o tilemap_demo
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
# Using make
|
||||
make run
|
||||
|
||||
# Or directly
|
||||
./build/tilemap_demo
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
```cpp
|
||||
#include "tilemap.h"
|
||||
|
||||
int main() {
|
||||
// Create a 4x4 tilemap (256x256 tiles total)
|
||||
TileMap tileMap(4, 4);
|
||||
|
||||
// Generate terrain using Perlin noise
|
||||
tileMap.generatePerlin(54321, 0.05f);
|
||||
|
||||
// Access individual tiles
|
||||
Tile& tile = tileMap.getTile(100, 100);
|
||||
tile.setType(Tile::WATER);
|
||||
|
||||
// Access chunks
|
||||
Chunk* chunk = tileMap.getChunk(1, 1);
|
||||
if (chunk) {
|
||||
chunk->setTile(32, 32, Tile(Tile::STONE));
|
||||
}
|
||||
|
||||
// Print map overview
|
||||
tileMap.printMap();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Key Classes
|
||||
|
||||
### Tile
|
||||
- Represents a single tile with a type (Empty, Grass, Stone, Water, Sand, Forest)
|
||||
- Provides utility methods like `isWalkable()` and `isLiquid()`
|
||||
|
||||
### Chunk
|
||||
- Contains a 64x64 array of tiles
|
||||
- Supports Perlin noise generation for natural terrain
|
||||
- Manages local tile coordinates within the chunk
|
||||
|
||||
### TileMap
|
||||
- Manages multiple chunks to form a complete world
|
||||
- Handles coordinate conversion between world and chunk coordinates
|
||||
- Provides unified access to tiles across chunk boundaries
|
||||
|
||||
### Random
|
||||
- Utility class for random number generation
|
||||
- Implements Perlin noise for natural terrain generation
|
||||
- Supports seeded generation for reproducible results
|
||||
|
||||
## Coordinate System
|
||||
|
||||
- **World Coordinates**: Global tile positions (0,0) to (worldWidth-1, worldHeight-1)
|
||||
- **Chunk Coordinates**: Chunk positions (0,0) to (mapWidth-1, mapHeight-1)
|
||||
- **Local Coordinates**: Tile positions within a chunk (0,0) to (63,63)
|
||||
|
||||
The system automatically converts between coordinate systems as needed.
|
||||
|
||||
## Customization
|
||||
|
||||
You can easily extend the system by:
|
||||
- Adding new tile types to the `Tile::Type` enum
|
||||
- Implementing custom generation algorithms in `Chunk`
|
||||
- Modifying tile properties and behaviors
|
||||
- Adding new terrain features or biomes
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- Each chunk contains 4,096 tiles (64×64)
|
||||
- Memory usage scales with the number of active chunks
|
||||
- Coordinate conversion is O(1)
|
||||
- Tile access within a chunk is O(1)
|
||||
|
@ -1,113 +1,3 @@
|
||||
# Tilemap Library
|
||||
|
||||
一个基于Perlin噪声和生物群系系统的C++地图生成库。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
tilemap/
|
||||
├── include/ # 库头文件
|
||||
│ ├── biome.h # 生物群系系统
|
||||
│ ├── chunk.h # 区块和瓦片定义
|
||||
│ ├── generation.h # 地形生成器
|
||||
│ ├── noise.h # Perlin噪声实现
|
||||
│ ├── tile.h # 瓦片类型定义
|
||||
│ └── tilemap.h # 地图容器类
|
||||
├── src/ # 库源文件
|
||||
│ ├── biome.cpp # 生物群系实现
|
||||
│ ├── generation.cpp # 地形生成实现
|
||||
│ ├── noise.cpp # Perlin噪声实现
|
||||
│ └── tilemap.cpp # 地图容器实现
|
||||
├── examples/ # 示例程序
|
||||
│ ├── basic_demo.cpp # 基础功能演示
|
||||
│ ├── advanced_demo.cpp # 高级功能演示
|
||||
│ ├── biome_demo.cpp # 生物群系演示
|
||||
│ ├── advanced_biome_demo.cpp # 高级生物群系分析
|
||||
│ └── CMakeLists.txt # 示例程序构建配置
|
||||
└── CMakeLists.txt # 主构建配置
|
||||
```
|
||||
|
||||
## 构建
|
||||
|
||||
### 构建库和示例程序
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
### 仅构建库(不构建示例)
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DBUILD_EXAMPLES=OFF ..
|
||||
make
|
||||
```
|
||||
|
||||
## 运行示例
|
||||
|
||||
构建完成后,可执行文件位于 `build/tilemap/examples/` 目录下:
|
||||
|
||||
```bash
|
||||
# 基础演示
|
||||
./build/tilemap/examples/basic_demo
|
||||
|
||||
# 高级功能演示
|
||||
./build/tilemap/examples/advanced_demo
|
||||
|
||||
# 生物群系系统演示
|
||||
./build/tilemap/examples/biome_demo
|
||||
|
||||
# 高级生物群系分析
|
||||
./build/tilemap/examples/advanced_biome_demo
|
||||
```
|
||||
|
||||
## 库使用
|
||||
|
||||
### 基本用法
|
||||
|
||||
```cpp
|
||||
#include "tilemap.h"
|
||||
#include "generation.h"
|
||||
|
||||
// 创建一个10x10区块的地图
|
||||
istd::TileMap tilemap(10);
|
||||
|
||||
// 配置生成参数
|
||||
istd::GenerationConfig config;
|
||||
config.seed = 42;
|
||||
|
||||
// 生成地图
|
||||
istd::map_generate(tilemap, config);
|
||||
```
|
||||
|
||||
### 使用生物群系系统
|
||||
|
||||
```cpp
|
||||
#include "generation.h"
|
||||
|
||||
istd::GenerationConfig config;
|
||||
config.seed = 12345;
|
||||
config.temperature_scale = 0.005; // 温度变化尺度
|
||||
config.humidity_scale = 0.007; // 湿度变化尺度
|
||||
|
||||
istd::TerrainGenerator generator(config);
|
||||
generator.generate_map(tilemap);
|
||||
```
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **Perlin噪声地形生成**: 生成自然的地形特征
|
||||
- **生物群系系统**: 基于温度和湿度的9种生物群系
|
||||
- **区块系统**: 支持大型地图的高效存储
|
||||
- **子区块分级**: 16x16瓦片的子区块,每个具有一致的生物群系
|
||||
- **高性能**: 优化的内存使用和生成算法
|
||||
|
||||
## 编译要求
|
||||
|
||||
- C++23标准支持
|
||||
- CMake 3.27或更高版本
|
||||
- 支持C++23的编译器(GCC 13+, Clang 16+, MSVC 2022+)
|
||||
The Tilemap System use in Instructed. Generates 2D tilemaps with biomes and features.
|
||||
|
245
tilemap/docs/api.md
Normal file
245
tilemap/docs/api.md
Normal file
@ -0,0 +1,245 @@
|
||||
# Tilemap Library API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The tilemap library provides a flexible system for generating and managing tile-based terrain with biome support. The library consists of several main components:
|
||||
|
||||
- **TileMap**: The main map container holding chunks of tiles
|
||||
- **Chunk**: 64x64 tile containers with biome information
|
||||
- **Tile**: Individual map tiles with base and surface types
|
||||
- **TerrainGenerator**: Procedural terrain generation system
|
||||
- **Biome System**: Climate-based terrain variation
|
||||
|
||||
## Core Classes
|
||||
|
||||
### TileMap
|
||||
|
||||
The main container for the entire map, organized as an n×n grid of chunks.
|
||||
|
||||
```cpp
|
||||
class TileMap {
|
||||
public:
|
||||
explicit TileMap(std::uint8_t size);
|
||||
|
||||
std::uint8_t get_size() const;
|
||||
Chunk& get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y);
|
||||
const Chunk& get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y) const;
|
||||
|
||||
Tile& get_tile(const TilePos& pos);
|
||||
const Tile& get_tile(const TilePos& pos) const;
|
||||
void set_tile(const TilePos& pos, const Tile& tile);
|
||||
};
|
||||
```
|
||||
|
||||
**Constructor Parameters:**
|
||||
- `size`: Number of chunks per side (max 100), creating an n×n grid
|
||||
|
||||
### Chunk
|
||||
|
||||
Each chunk contains 64×64 tiles and 4×4 sub-chunk biome information.
|
||||
|
||||
```cpp
|
||||
struct Chunk {
|
||||
static constexpr uint8_t size = 64; // Tiles per side
|
||||
static constexpr uint8_t subchunk_size = 16; // Tiles per sub-chunk side
|
||||
static constexpr uint8_t subchunk_count = 4; // Sub-chunks per side
|
||||
|
||||
Tile tiles[size][size]; // 64x64 tile grid
|
||||
BiomeType biome[subchunk_count][subchunk_count]; // 4x4 biome grid
|
||||
};
|
||||
```
|
||||
|
||||
### Tile
|
||||
|
||||
Individual map tiles with base terrain and surface features.
|
||||
|
||||
```cpp
|
||||
struct Tile {
|
||||
BaseTileType base : 4; // Base terrain type
|
||||
SurfaceTileType surface : 4; // Surface features
|
||||
};
|
||||
```
|
||||
|
||||
**Base Tile Types:**
|
||||
- `Land`: Standard ground terrain
|
||||
- `Mountain`: Rocky elevated terrain
|
||||
- `Sand`: Desert/beach terrain
|
||||
- `Water`: Water bodies
|
||||
- `Ice`: Frozen terrain
|
||||
|
||||
**Surface Tile Types:**
|
||||
- `Empty`: No surface features
|
||||
- `Wood`: Trees/vegetation
|
||||
- `Structure`: Player-built structures
|
||||
|
||||
### TilePos
|
||||
|
||||
Position structure for locating tiles within the map.
|
||||
|
||||
```cpp
|
||||
struct TilePos {
|
||||
uint8_t chunk_x; // Chunk X coordinate
|
||||
uint8_t chunk_y; // Chunk Y coordinate
|
||||
uint8_t local_x; // Tile X within chunk (0-63)
|
||||
uint8_t local_y; // Tile Y within chunk (0-63)
|
||||
};
|
||||
```
|
||||
|
||||
## Terrain Generation
|
||||
|
||||
### GenerationConfig
|
||||
|
||||
Configuration parameters for terrain generation.
|
||||
|
||||
```cpp
|
||||
struct GenerationConfig {
|
||||
std::uint64_t seed = 0; // Random seed
|
||||
double temperature_scale = 0.005; // Temperature noise scale
|
||||
double humidity_scale = 0.007; // Humidity noise scale
|
||||
double base_scale = 0.08; // Base terrain noise scale
|
||||
};
|
||||
```
|
||||
|
||||
### TerrainGenerator
|
||||
|
||||
Main class for procedural terrain generation.
|
||||
|
||||
```cpp
|
||||
class TerrainGenerator {
|
||||
public:
|
||||
explicit TerrainGenerator(const GenerationConfig& config);
|
||||
void generate_map(TileMap& tilemap);
|
||||
};
|
||||
```
|
||||
|
||||
### Generation Function
|
||||
|
||||
Convenience function for map generation.
|
||||
|
||||
```cpp
|
||||
void map_generate(TileMap& tilemap, const GenerationConfig& config);
|
||||
```
|
||||
|
||||
## Biome System
|
||||
|
||||
### BiomeType
|
||||
|
||||
Available biome types based on temperature and humidity.
|
||||
|
||||
```cpp
|
||||
enum class BiomeType : std::uint8_t {
|
||||
SnowyPeeks, // Cold & Dry
|
||||
SnowyPlains, // Cold & Moderate
|
||||
FrozenOcean, // Cold & Wet
|
||||
Plains, // Temperate & Dry
|
||||
Forest, // Temperate & Moderate
|
||||
Ocean, // Temperate & Wet
|
||||
Desert, // Hot & Dry
|
||||
Savanna, // Hot & Moderate
|
||||
LukeOcean, // Hot & Wet
|
||||
};
|
||||
```
|
||||
|
||||
### BiomeProperties
|
||||
|
||||
Properties that control terrain generation for each biome.
|
||||
|
||||
```cpp
|
||||
struct BiomeProperties {
|
||||
std::string_view name; // Biome name
|
||||
double water_ratio; // Water generation ratio
|
||||
double ice_ratio; // Ice generation ratio
|
||||
double sand_ratio; // Sand generation ratio
|
||||
double land_ratio; // Land generation ratio
|
||||
int base_octaves = 3; // Noise octaves
|
||||
double base_persistence = 0.5; // Noise persistence
|
||||
};
|
||||
```
|
||||
|
||||
### Biome Functions
|
||||
|
||||
```cpp
|
||||
const BiomeProperties& get_biome_properties(BiomeType biome);
|
||||
BiomeType determine_biome(double temperature, double humidity);
|
||||
```
|
||||
|
||||
### SubChunkPos
|
||||
|
||||
Position within a chunk's 4×4 sub-chunk grid.
|
||||
|
||||
```cpp
|
||||
struct SubChunkPos {
|
||||
std::uint8_t sub_x; // 0-3
|
||||
std::uint8_t sub_y; // 0-3
|
||||
};
|
||||
|
||||
constexpr std::pair<std::uint8_t, std::uint8_t> subchunk_to_tile_start(
|
||||
const SubChunkPos& pos
|
||||
);
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Map Generation
|
||||
|
||||
```cpp
|
||||
#include "tilemap.h"
|
||||
#include "generation.h"
|
||||
|
||||
// Create a 4x4 chunk map
|
||||
istd::TileMap tilemap(4);
|
||||
|
||||
// Configure generation
|
||||
istd::GenerationConfig config;
|
||||
config.seed = 12345;
|
||||
config.temperature_scale = 0.005;
|
||||
config.humidity_scale = 0.007;
|
||||
config.base_scale = 0.08;
|
||||
|
||||
// Generate terrain
|
||||
istd::map_generate(tilemap, config);
|
||||
|
||||
// Access tiles
|
||||
for (int chunk_y = 0; chunk_y < 4; ++chunk_y) {
|
||||
for (int chunk_x = 0; chunk_x < 4; ++chunk_x) {
|
||||
const auto& chunk = tilemap.get_chunk(chunk_x, chunk_y);
|
||||
// Process chunk tiles...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing Individual Tiles
|
||||
|
||||
```cpp
|
||||
// Using TilePos
|
||||
istd::TilePos pos{0, 0, 32, 32}; // Chunk (0,0), tile (32,32)
|
||||
const auto& tile = tilemap.get_tile(pos);
|
||||
|
||||
// Direct chunk access
|
||||
const auto& chunk = tilemap.get_chunk(0, 0);
|
||||
const auto& tile2 = chunk.tiles[32][32];
|
||||
```
|
||||
|
||||
### Working with Biomes
|
||||
|
||||
```cpp
|
||||
// Get biome for a sub-chunk
|
||||
const auto& chunk = tilemap.get_chunk(0, 0);
|
||||
istd::BiomeType biome = chunk.biome[1][1]; // Sub-chunk (1,1)
|
||||
|
||||
// Get biome properties
|
||||
const auto& props = istd::get_biome_properties(biome);
|
||||
std::cout << "Biome: " << props.name << std::endl;
|
||||
```
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- Each chunk contains 4,096 tiles (64×64)
|
||||
- A 4×4 chunk map contains 65,536 total tiles
|
||||
- Sub-chunks provide efficient biome management (16×16 tile regions)
|
||||
- Tiles are packed into 1 byte each for memory efficiency
|
||||
- Generation uses Perlin noise for natural-looking terrain
|
||||
|
||||
## Thread Safety
|
||||
|
||||
The library is not inherently thread-safe. External synchronization is required for concurrent access to TileMap objects.
|
@ -3,12 +3,12 @@ cmake_minimum_required(VERSION 3.27)
|
||||
# Examples for the tilemap library
|
||||
# Each example is built as a separate executable
|
||||
|
||||
# Dual-noise terrain generation demonstration
|
||||
add_executable(dual_noise_demo dual_noise_demo.cpp)
|
||||
target_link_libraries(dual_noise_demo PRIVATE istd_tilemap)
|
||||
target_include_directories(dual_noise_demo PRIVATE ../include)
|
||||
|
||||
# Biome system demonstration
|
||||
add_executable(biome_demo biome_demo.cpp)
|
||||
target_link_libraries(biome_demo PRIVATE istd_tilemap)
|
||||
target_include_directories(biome_demo PRIVATE ../include)
|
||||
|
||||
# Perlin noise visualization
|
||||
add_executable(perlin_demo perlin_demo.cpp)
|
||||
target_link_libraries(perlin_demo PRIVATE istd_tilemap)
|
||||
target_include_directories(perlin_demo PRIVATE ../include)
|
||||
|
@ -1,187 +1,202 @@
|
||||
#include "biome.h"
|
||||
#include "generation.h"
|
||||
#include "tile.h"
|
||||
#include "tilemap.h"
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace istd;
|
||||
|
||||
// Function to get character representation for biome visualization
|
||||
char get_biome_char(BiomeType biome) {
|
||||
switch (biome) {
|
||||
case BiomeType::Desert:
|
||||
return 'D';
|
||||
case BiomeType::Savanna:
|
||||
return 'S';
|
||||
case BiomeType::TropicalRainforest:
|
||||
return 'T';
|
||||
case BiomeType::Grassland:
|
||||
return 'G';
|
||||
case BiomeType::DeciduousForest:
|
||||
return 'F';
|
||||
case BiomeType::TemperateRainforest:
|
||||
return 'R';
|
||||
case BiomeType::Tundra:
|
||||
return 'U';
|
||||
case BiomeType::Taiga:
|
||||
return 'A';
|
||||
case BiomeType::FrozenOcean:
|
||||
return 'O';
|
||||
// Color mapping for different base tile types
|
||||
const char *get_tile_color(istd::BaseTileType type) {
|
||||
switch (type) {
|
||||
case istd::BaseTileType::Land:
|
||||
return "#90EE90"; // Light green
|
||||
case istd::BaseTileType::Mountain:
|
||||
return "#8B4513"; // Saddle brown
|
||||
case istd::BaseTileType::Sand:
|
||||
return "#F4A460"; // Sandy brown
|
||||
case istd::BaseTileType::Water:
|
||||
return "#1E90FF"; // Dodger blue
|
||||
case istd::BaseTileType::Ice:
|
||||
return "#B0E0E6"; // Powder blue
|
||||
default:
|
||||
return "#808080"; // Gray for unknown types
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
// Function to get biome name as string
|
||||
const char *get_biome_name(BiomeType biome) {
|
||||
switch (biome) {
|
||||
case BiomeType::Desert:
|
||||
return "Desert";
|
||||
case BiomeType::Savanna:
|
||||
return "Savanna";
|
||||
case BiomeType::TropicalRainforest:
|
||||
return "Tropical Rainforest";
|
||||
case BiomeType::Grassland:
|
||||
return "Grassland";
|
||||
case BiomeType::DeciduousForest:
|
||||
return "Deciduous Forest";
|
||||
case BiomeType::TemperateRainforest:
|
||||
return "Temperate Rainforest";
|
||||
case BiomeType::Tundra:
|
||||
return "Tundra";
|
||||
case BiomeType::Taiga:
|
||||
return "Taiga";
|
||||
case BiomeType::FrozenOcean:
|
||||
return "Frozen Ocean";
|
||||
// Generate SVG file from tilemap
|
||||
void generate_svg(const istd::TileMap &tilemap, const std::string &filename) {
|
||||
std::ofstream file(filename);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error: Could not open output file: " << filename
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
return "Unknown";
|
||||
|
||||
const int chunks_per_side = tilemap.get_size();
|
||||
const int tiles_per_chunk = istd::Chunk::size;
|
||||
const int total_tiles = chunks_per_side * tiles_per_chunk;
|
||||
const int tile_size = 2; // Size of each tile in SVG pixels
|
||||
const int svg_size = total_tiles * tile_size;
|
||||
|
||||
// SVG header
|
||||
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
file << "<svg width=\"" << svg_size << "\" height=\"" << svg_size
|
||||
<< "\" xmlns=\"http://www.w3.org/2000/svg\">\n";
|
||||
file << "<title>Tilemap Visualization</title>\n";
|
||||
|
||||
// Generate tiles
|
||||
for (int chunk_y = 0; chunk_y < chunks_per_side; ++chunk_y) {
|
||||
for (int chunk_x = 0; chunk_x < chunks_per_side; ++chunk_x) {
|
||||
const auto &chunk = tilemap.get_chunk(chunk_x, chunk_y);
|
||||
|
||||
for (int tile_y = 0; tile_y < tiles_per_chunk; ++tile_y) {
|
||||
for (int tile_x = 0; tile_x < tiles_per_chunk; ++tile_x) {
|
||||
const auto &tile = chunk.tiles[tile_x][tile_y];
|
||||
|
||||
int global_x = chunk_x * tiles_per_chunk + tile_x;
|
||||
int global_y = chunk_y * tiles_per_chunk + tile_y;
|
||||
|
||||
int svg_x = global_x * tile_size;
|
||||
int svg_y = global_y * tile_size;
|
||||
|
||||
const char *color = get_tile_color(tile.base);
|
||||
|
||||
file << "<rect x=\"" << svg_x << "\" y=\"" << svg_y
|
||||
<< "\" width=\"" << tile_size << "\" height=\""
|
||||
<< tile_size << "\" fill=\"" << color << "\"/>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add grid lines for chunk boundaries
|
||||
file << "<!-- Chunk boundaries -->\n";
|
||||
for (int i = 0; i <= chunks_per_side; ++i) {
|
||||
int pos = i * tiles_per_chunk * tile_size;
|
||||
// Vertical lines
|
||||
file << "<line x1=\"" << pos << "\" y1=\"0\" x2=\"" << pos << "\" y2=\""
|
||||
<< svg_size << "\" stroke=\"black\" stroke-width=\"2\"/>\n";
|
||||
// Horizontal lines
|
||||
file << "<line x1=\"0\" y1=\"" << pos << "\" x2=\"" << svg_size
|
||||
<< "\" y2=\"" << pos
|
||||
<< "\" stroke=\"black\" stroke-width=\"2\"/>\n";
|
||||
}
|
||||
|
||||
// Legend
|
||||
file << "<!-- Legend -->\n";
|
||||
file << "<g transform=\"translate(10, 10)\">\n";
|
||||
file << "<rect x=\"0\" y=\"0\" width=\"200\" height=\"140\" fill=\"white\" "
|
||||
"stroke=\"black\" stroke-width=\"1\" opacity=\"0.9\"/>\n";
|
||||
file << "<text x=\"10\" y=\"20\" font-family=\"Arial\" font-size=\"14\" "
|
||||
"font-weight=\"bold\">Legend</text>\n";
|
||||
|
||||
const std::pair<istd::BaseTileType, const char *> legend_items[] = {
|
||||
{istd::BaseTileType::Land, "Land" },
|
||||
{istd::BaseTileType::Mountain, "Mountain"},
|
||||
{istd::BaseTileType::Sand, "Sand" },
|
||||
{istd::BaseTileType::Water, "Water" },
|
||||
{istd::BaseTileType::Ice, "Ice" }
|
||||
};
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
int y_pos = 40 + i * 20;
|
||||
file << "<rect x=\"10\" y=\"" << (y_pos - 10)
|
||||
<< "\" width=\"15\" height=\"15\" fill=\""
|
||||
<< get_tile_color(legend_items[i].first)
|
||||
<< "\" stroke=\"black\" stroke-width=\"1\"/>\n";
|
||||
file << "<text x=\"30\" y=\"" << y_pos
|
||||
<< "\" font-family=\"Arial\" font-size=\"12\">"
|
||||
<< legend_items[i].second << "</text>\n";
|
||||
}
|
||||
file << "</g>\n";
|
||||
|
||||
file << "</svg>\n";
|
||||
file.close();
|
||||
|
||||
std::cout << "SVG file generated: " << filename << std::endl;
|
||||
std::cout << "Tilemap size: " << total_tiles << "x" << total_tiles
|
||||
<< " tiles" << std::endl;
|
||||
std::cout << "Chunks: " << chunks_per_side << "x" << chunks_per_side
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "=== Biome System Demo ===" << std::endl;
|
||||
std::cout << "This demo shows biome distribution based on temperature and "
|
||||
"humidity"
|
||||
<< std::endl;
|
||||
std::cout << "Legend:" << std::endl;
|
||||
std::cout << " D = Desert S = Savanna T = Tropical"
|
||||
<< std::endl;
|
||||
std::cout << " G = Grassland F = Deciduous R = Temp.Rain"
|
||||
<< std::endl;
|
||||
std::cout << " U = Tundra A = Taiga O = Frozen"
|
||||
<< std::endl;
|
||||
std::cout << std::endl;
|
||||
// Print statistics about the generated map
|
||||
void print_statistics(const istd::TileMap &tilemap) {
|
||||
int tile_counts[5] = {0}; // Count for each base tile type
|
||||
const int chunks_per_side = tilemap.get_size();
|
||||
const int tiles_per_chunk = istd::Chunk::size;
|
||||
|
||||
// Create a tilemap for biome demonstration
|
||||
constexpr std::uint8_t map_size = 3; // 3x3 chunks for compact display
|
||||
TileMap tilemap(map_size);
|
||||
for (int chunk_y = 0; chunk_y < chunks_per_side; ++chunk_y) {
|
||||
for (int chunk_x = 0; chunk_x < chunks_per_side; ++chunk_x) {
|
||||
const auto &chunk = tilemap.get_chunk(chunk_x, chunk_y);
|
||||
|
||||
// Configure generation with good biome variety
|
||||
GenerationConfig config;
|
||||
config.seed = 98765;
|
||||
config.temperature_scale = 0.003; // Larger temperature zones
|
||||
config.humidity_scale = 0.004; // Larger humidity zones
|
||||
for (int tile_y = 0; tile_y < tiles_per_chunk; ++tile_y) {
|
||||
for (int tile_x = 0; tile_x < tiles_per_chunk; ++tile_x) {
|
||||
const auto &tile = chunk.tiles[tile_x][tile_y];
|
||||
tile_counts[static_cast<int>(tile.base)]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Generating " << static_cast<int>(map_size) << "x"
|
||||
<< static_cast<int>(map_size) << " chunks..." << std::endl;
|
||||
const char *tile_names[] = {"Land", "Mountain", "Sand", "Water", "Ice"};
|
||||
int total_tiles
|
||||
= chunks_per_side * chunks_per_side * tiles_per_chunk * tiles_per_chunk;
|
||||
|
||||
std::cout << "\nTile Statistics:\n";
|
||||
std::cout << "================\n";
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
double percentage = (double)tile_counts[i] / total_tiles * 100.0;
|
||||
std::cout << std::setw(10) << tile_names[i] << ": " << std::setw(8)
|
||||
<< tile_counts[i] << " (" << std::fixed
|
||||
<< std::setprecision(1) << percentage << "%)\n";
|
||||
}
|
||||
std::cout << "Total tiles: " << total_tiles << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Parse command line arguments
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " <seed> <output_file.svg>\n";
|
||||
std::cerr << "Example: " << argv[0] << " 12345 output.svg\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint64_t seed = std::strtoull(argv[1], nullptr, 10);
|
||||
std::string output_filename = argv[2];
|
||||
|
||||
// Validate output filename
|
||||
if (output_filename.length() < 4
|
||||
|| output_filename.substr(output_filename.length() - 4) != ".svg") {
|
||||
std::cerr << "Error: Output filename must end with .svg\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Generating 4x4 chunk tilemap with seed: " << seed
|
||||
<< std::endl;
|
||||
|
||||
// Create 4x4 chunk tilemap
|
||||
istd::TileMap tilemap(4);
|
||||
|
||||
// Configure generation parameters
|
||||
istd::GenerationConfig config;
|
||||
config.seed = seed;
|
||||
config.temperature_scale = 0.005;
|
||||
config.humidity_scale = 0.007;
|
||||
config.base_scale = 0.08;
|
||||
|
||||
// Generate the map
|
||||
map_generate(tilemap, config);
|
||||
std::cout << "Generating terrain..." << std::endl;
|
||||
istd::map_generate(tilemap, config);
|
||||
|
||||
std::cout << "Generation complete!" << std::endl << std::endl;
|
||||
// Generate SVG output
|
||||
std::cout << "Creating SVG visualization..." << std::endl;
|
||||
generate_svg(tilemap, output_filename);
|
||||
|
||||
// Show biome properties
|
||||
std::cout << "=== Biome Properties ===" << std::endl;
|
||||
const BiomeType all_biomes[]
|
||||
= {BiomeType::Desert,
|
||||
BiomeType::Savanna,
|
||||
BiomeType::TropicalRainforest,
|
||||
BiomeType::Grassland,
|
||||
BiomeType::DeciduousForest,
|
||||
BiomeType::TemperateRainforest,
|
||||
BiomeType::Tundra,
|
||||
BiomeType::Taiga,
|
||||
BiomeType::FrozenOcean};
|
||||
|
||||
for (BiomeType biome : all_biomes) {
|
||||
const BiomeProperties &props = get_biome_properties(biome);
|
||||
std::cout << get_biome_name(biome) << " (" << get_biome_char(biome)
|
||||
<< "):" << std::endl;
|
||||
std::cout << " Base terrain - Scale: " << props.base_scale
|
||||
<< " Octaves: " << props.base_octaves
|
||||
<< " Persistence: " << props.base_persistence << std::endl;
|
||||
std::cout << " Surface features - Scale: " << props.surface_scale
|
||||
<< " Octaves: " << props.surface_octaves
|
||||
<< " Persistence: " << props.surface_persistence << std::endl;
|
||||
std::cout << " Thresholds - Water: " << props.water_threshold
|
||||
<< " Sand: " << props.sand_threshold
|
||||
<< " Mountain: " << props.mountain_threshold << std::endl;
|
||||
std::cout << " Features - Wood: " << props.wood_threshold
|
||||
<< " Snow: " << props.snow_threshold << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Sample some actual generated tiles to show the system working
|
||||
std::cout << "=== Sample Generated Tiles ===" << std::endl;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
std::uint8_t chunk_x = i % map_size;
|
||||
std::uint8_t chunk_y = i / map_size;
|
||||
std::uint8_t local_x = 32; // Middle of chunk
|
||||
std::uint8_t local_y = 32;
|
||||
|
||||
TilePos pos{chunk_x, chunk_y, local_x, local_y};
|
||||
Tile tile = tilemap.get_tile(pos);
|
||||
|
||||
std::cout << "Chunk (" << static_cast<int>(chunk_x) << ","
|
||||
<< static_cast<int>(chunk_y) << "): ";
|
||||
|
||||
// Convert base and surface types to readable strings
|
||||
const char *base_names[] = {"Land", "Mountain", "Sand", "Water", "Ice"};
|
||||
const char *surface_names[] = {"Empty", "Wood", "Snow"};
|
||||
|
||||
std::cout << "Base=" << base_names[static_cast<int>(tile.base)]
|
||||
<< " Surface="
|
||||
<< surface_names[static_cast<int>(tile.surface)] << std::endl;
|
||||
}
|
||||
|
||||
// Show climate zones demonstration
|
||||
std::cout << std::endl << "=== Climate Zone Demonstration ===" << std::endl;
|
||||
std::cout
|
||||
<< "Temperature/Humidity grid (each position shows resulting biome):"
|
||||
<< std::endl;
|
||||
std::cout << "Humidity →" << std::endl;
|
||||
|
||||
for (int temp = 0; temp < 3; ++temp) {
|
||||
if (temp == 1) {
|
||||
std::cout << "T ";
|
||||
} else {
|
||||
std::cout << "e ";
|
||||
}
|
||||
|
||||
for (int humid = 0; humid < 3; ++humid) {
|
||||
double temperature
|
||||
= static_cast<double>(temp) / 2.0; // 0.0, 0.5, 1.0
|
||||
double humidity = static_cast<double>(humid) / 2.0;
|
||||
|
||||
BiomeType biome = determine_biome(temperature, humidity);
|
||||
std::cout << get_biome_char(biome) << " ";
|
||||
}
|
||||
|
||||
if (temp == 1) {
|
||||
std::cout << " ← Temperature";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
if (temp == 0) {
|
||||
std::cout << "m ";
|
||||
} else if (temp == 2) {
|
||||
std::cout << "p ";
|
||||
} else {
|
||||
std::cout << " ";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Where: Cold(top) → Hot(bottom), Dry(left) → Wet(right)"
|
||||
<< std::endl;
|
||||
// Print statistics
|
||||
print_statistics(tilemap);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
#include "generation.h"
|
||||
#include "tilemap.h"
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
using namespace istd;
|
||||
|
||||
// Function to get character representation of base tile type
|
||||
char get_base_char(BaseTileType base_type) {
|
||||
switch (base_type) {
|
||||
case BaseTileType::Land:
|
||||
return '.';
|
||||
case BaseTileType::Mountain:
|
||||
return '^';
|
||||
case BaseTileType::Sand:
|
||||
return '~';
|
||||
case BaseTileType::Water:
|
||||
return 'W';
|
||||
case BaseTileType::Ice:
|
||||
return 'I';
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
// Function to get character representation of surface tile type
|
||||
char get_surface_char(SurfaceTileType surface_type) {
|
||||
switch (surface_type) {
|
||||
case SurfaceTileType::Empty:
|
||||
return ' ';
|
||||
case SurfaceTileType::Wood:
|
||||
return 'T';
|
||||
case SurfaceTileType::Snow:
|
||||
return 'S';
|
||||
case SurfaceTileType::Structure:
|
||||
return 'H';
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
// Function to get combined display character for a tile
|
||||
char get_tile_char(const Tile &tile) {
|
||||
// Surface features take priority for display
|
||||
if (tile.surface != SurfaceTileType::Empty) {
|
||||
return get_surface_char(tile.surface);
|
||||
}
|
||||
return get_base_char(tile.base);
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "=== Dual-Noise Terrain Generation Demo ===" << std::endl;
|
||||
std::cout << "This demo shows the new base + surface generation system"
|
||||
<< std::endl;
|
||||
std::cout << "Legend:" << std::endl;
|
||||
std::cout
|
||||
<< " . = Land ^ = Mountain ~ = Sand W = Water I = Ice"
|
||||
<< std::endl;
|
||||
std::cout << " T = Trees S = Snow H = Structure" << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
// Create a small tilemap for demonstration
|
||||
constexpr std::uint8_t map_size = 4; // 4x4 chunks
|
||||
TileMap tilemap(map_size);
|
||||
|
||||
// Configure generation with different seeds for variety
|
||||
GenerationConfig config;
|
||||
config.seed = 12345;
|
||||
config.temperature_scale = 0.008; // Slightly larger temperature variation
|
||||
config.humidity_scale = 0.006; // Slightly larger humidity variation
|
||||
|
||||
std::cout << "Generating " << static_cast<int>(map_size) << "x"
|
||||
<< static_cast<int>(map_size) << " chunks ("
|
||||
<< static_cast<int>(map_size * Chunk::size) << "x"
|
||||
<< static_cast<int>(map_size * Chunk::size) << " tiles)..."
|
||||
<< std::endl;
|
||||
|
||||
// Generate the map
|
||||
map_generate(tilemap, config);
|
||||
|
||||
std::cout << "Generation complete!" << std::endl << std::endl;
|
||||
|
||||
// Display the entire map
|
||||
std::cout << "Complete Map:" << std::endl;
|
||||
for (std::uint8_t global_y = 0; global_y < map_size * Chunk::size;
|
||||
++global_y) {
|
||||
for (std::uint8_t global_x = 0; global_x < map_size * Chunk::size;
|
||||
++global_x) {
|
||||
// Convert global coordinates to chunk and local coordinates
|
||||
std::uint8_t chunk_x = global_x / Chunk::size;
|
||||
std::uint8_t chunk_y = global_y / Chunk::size;
|
||||
std::uint8_t local_x = global_x % Chunk::size;
|
||||
std::uint8_t local_y = global_y % Chunk::size;
|
||||
|
||||
TilePos pos{chunk_x, chunk_y, local_x, local_y};
|
||||
Tile tile = tilemap.get_tile(pos);
|
||||
std::cout << get_tile_char(tile);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
// Show detailed analysis for each chunk
|
||||
std::cout << "=== Chunk-by-Chunk Analysis ===" << std::endl;
|
||||
for (std::uint8_t chunk_y = 0; chunk_y < map_size; ++chunk_y) {
|
||||
for (std::uint8_t chunk_x = 0; chunk_x < map_size; ++chunk_x) {
|
||||
std::cout << "Chunk (" << static_cast<int>(chunk_x) << ","
|
||||
<< static_cast<int>(chunk_y) << "):" << std::endl;
|
||||
|
||||
// Count tile types in this chunk
|
||||
int base_counts[5] = {0}; // Land, Mountain, Sand, Water, Ice
|
||||
int surface_counts[4] = {0}; // Empty, Wood, Snow, Structure
|
||||
|
||||
for (std::uint8_t local_y = 0; local_y < Chunk::size; ++local_y) {
|
||||
for (std::uint8_t local_x = 0; local_x < Chunk::size;
|
||||
++local_x) {
|
||||
TilePos pos{chunk_x, chunk_y, local_x, local_y};
|
||||
Tile tile = tilemap.get_tile(pos);
|
||||
|
||||
base_counts[static_cast<int>(tile.base)]++;
|
||||
surface_counts[static_cast<int>(tile.surface)]++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << " Base terrain: Land=" << base_counts[0]
|
||||
<< " Mountain=" << base_counts[1]
|
||||
<< " Sand=" << base_counts[2]
|
||||
<< " Water=" << base_counts[3]
|
||||
<< " Ice=" << base_counts[4] << std::endl;
|
||||
|
||||
std::cout << " Surface features: Empty=" << surface_counts[0]
|
||||
<< " Wood=" << surface_counts[1]
|
||||
<< " Snow=" << surface_counts[2]
|
||||
<< " Structure=" << surface_counts[3] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "=== Sample Detailed Tiles ===" << std::endl;
|
||||
|
||||
// Show some individual tile details
|
||||
std::vector<TilePos> sample_positions = {
|
||||
{0, 0, 10, 10},
|
||||
{1, 1, 30, 30},
|
||||
{2, 2, 50, 50},
|
||||
{3, 3, 60, 60}
|
||||
};
|
||||
|
||||
for (const auto &pos : sample_positions) {
|
||||
if (pos.chunk_x < map_size && pos.chunk_y < map_size
|
||||
&& pos.local_x < Chunk::size && pos.local_y < Chunk::size) {
|
||||
Tile tile = tilemap.get_tile(pos);
|
||||
std::cout << "Tile at chunk(" << static_cast<int>(pos.chunk_x)
|
||||
<< "," << static_cast<int>(pos.chunk_y) << ") local("
|
||||
<< static_cast<int>(pos.local_x) << ","
|
||||
<< static_cast<int>(pos.local_y) << "): ";
|
||||
|
||||
std::cout << "Base=" << get_base_char(tile.base)
|
||||
<< " Surface=" << get_surface_char(tile.surface)
|
||||
<< " Display=" << get_tile_char(tile) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
167
tilemap/examples/perlin_demo.cpp
Normal file
167
tilemap/examples/perlin_demo.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#include "noise.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
|
||||
// Convert a noise value [0,1] to a grayscale color
|
||||
std::string noise_to_grayscale(double noise_value) {
|
||||
// Clamp to [0,1] range
|
||||
noise_value = std::max(0.0, std::min(1.0, noise_value));
|
||||
|
||||
// Convert to 0-255 range
|
||||
int gray = static_cast<int>(noise_value * 255);
|
||||
|
||||
// Convert to hex color
|
||||
std::ostringstream oss;
|
||||
oss << "#" << std::hex << std::setfill('0') << std::setw(2) << gray
|
||||
<< std::setw(2) << gray << std::setw(2) << gray;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// Generate SVG visualization of Perlin noise
|
||||
void generate_noise_svg(const std::string& filename, int size, double scale,
|
||||
std::uint64_t seed, int octaves = 1, double persistence = 0.5) {
|
||||
std::ofstream file(filename);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error: Could not open output file: " << filename << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create noise generator
|
||||
istd::PerlinNoise noise(seed);
|
||||
|
||||
const int pixel_size = 2; // Size of each pixel in SVG units
|
||||
const int svg_size = size * pixel_size;
|
||||
|
||||
// SVG header
|
||||
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
file << "<svg width=\"" << svg_size << "\" height=\"" << svg_size
|
||||
<< "\" xmlns=\"http://www.w3.org/2000/svg\">\n";
|
||||
file << "<title>Perlin Noise Visualization (Scale: " << scale
|
||||
<< ", Octaves: " << octaves << ", Seed: " << seed << ")</title>\n";
|
||||
|
||||
// Generate noise values and create rectangles
|
||||
double min_value = 1.0, max_value = 0.0;
|
||||
|
||||
for (int y = 0; y < size; ++y) {
|
||||
for (int x = 0; x < size; ++x) {
|
||||
double noise_value;
|
||||
if (octaves == 1) {
|
||||
noise_value = noise.noise(x * scale, y * scale);
|
||||
} else {
|
||||
noise_value = noise.octave_noise(x * scale, y * scale, octaves, persistence);
|
||||
}
|
||||
|
||||
// Track min/max for statistics
|
||||
min_value = std::min(min_value, noise_value);
|
||||
max_value = std::max(max_value, noise_value);
|
||||
|
||||
std::string color = noise_to_grayscale(noise_value);
|
||||
|
||||
int svg_x = x * pixel_size;
|
||||
int svg_y = y * pixel_size;
|
||||
|
||||
file << "<rect x=\"" << svg_x << "\" y=\"" << svg_y
|
||||
<< "\" width=\"" << pixel_size << "\" height=\"" << pixel_size
|
||||
<< "\" fill=\"" << color << "\"/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Add information text
|
||||
file << "<!-- Statistics: Min=" << min_value << " Max=" << max_value << " -->\n";
|
||||
|
||||
// Add parameter info text overlay
|
||||
file << "<g transform=\"translate(10, 10)\">\n";
|
||||
file << "<rect x=\"0\" y=\"0\" width=\"300\" height=\"120\" fill=\"white\" stroke=\"black\" stroke-width=\"1\" opacity=\"0.9\"/>\n";
|
||||
file << "<text x=\"10\" y=\"20\" font-family=\"Arial\" font-size=\"14\" font-weight=\"bold\">Perlin Noise Parameters</text>\n";
|
||||
file << "<text x=\"10\" y=\"40\" font-family=\"Arial\" font-size=\"12\">Size: " << size << "x" << size << "</text>\n";
|
||||
file << "<text x=\"10\" y=\"55\" font-family=\"Arial\" font-size=\"12\">Scale: " << scale << "</text>\n";
|
||||
file << "<text x=\"10\" y=\"70\" font-family=\"Arial\" font-size=\"12\">Seed: " << seed << "</text>\n";
|
||||
file << "<text x=\"10\" y=\"85\" font-family=\"Arial\" font-size=\"12\">Octaves: " << octaves << "</text>\n";
|
||||
file << "<text x=\"10\" y=\"100\" font-family=\"Arial\" font-size=\"12\">Range: ["
|
||||
<< std::fixed << std::setprecision(3) << min_value << ", " << max_value << "]</text>\n";
|
||||
file << "</g>\n";
|
||||
|
||||
// Add grayscale legend
|
||||
file << "<g transform=\"translate(" << (svg_size - 60) << ", 10)\">\n";
|
||||
file << "<text x=\"0\" y=\"15\" font-family=\"Arial\" font-size=\"12\" font-weight=\"bold\">Value</text>\n";
|
||||
for (int i = 0; i <= 10; ++i) {
|
||||
double value = i / 10.0;
|
||||
std::string color = noise_to_grayscale(value);
|
||||
int y_pos = 20 + i * 15;
|
||||
file << "<rect x=\"0\" y=\"" << y_pos << "\" width=\"20\" height=\"12\" fill=\""
|
||||
<< color << "\" stroke=\"black\" stroke-width=\"0.5\"/>\n";
|
||||
file << "<text x=\"25\" y=\"" << (y_pos + 9) << "\" font-family=\"Arial\" font-size=\"10\">"
|
||||
<< std::fixed << std::setprecision(1) << value << "</text>\n";
|
||||
}
|
||||
file << "</g>\n";
|
||||
|
||||
file << "</svg>\n";
|
||||
file.close();
|
||||
|
||||
std::cout << "Perlin noise SVG generated: " << filename << std::endl;
|
||||
std::cout << "Size: " << size << "x" << size << " pixels" << std::endl;
|
||||
std::cout << "Scale: " << scale << ", Octaves: " << octaves << std::endl;
|
||||
std::cout << "Value range: [" << std::fixed << std::setprecision(3)
|
||||
<< min_value << ", " << max_value << "]" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Default parameters
|
||||
std::uint64_t seed = 12345;
|
||||
std::string output_filename = "perlin_noise.svg";
|
||||
double scale = 0.02;
|
||||
int octaves = 1;
|
||||
double persistence = 0.5;
|
||||
|
||||
// Parse command line arguments
|
||||
if (argc >= 2) {
|
||||
seed = std::strtoull(argv[1], nullptr, 10);
|
||||
}
|
||||
if (argc >= 3) {
|
||||
output_filename = argv[2];
|
||||
}
|
||||
if (argc >= 4) {
|
||||
scale = std::strtod(argv[3], nullptr);
|
||||
}
|
||||
if (argc >= 5) {
|
||||
octaves = std::strtol(argv[4], nullptr, 10);
|
||||
}
|
||||
if (argc >= 6) {
|
||||
persistence = std::strtod(argv[5], nullptr);
|
||||
}
|
||||
|
||||
if (argc == 1 || argc > 6) {
|
||||
std::cout << "Usage: " << argv[0] << " [seed] [output.svg] [scale] [octaves] [persistence]\n";
|
||||
std::cout << "Defaults: seed=12345, output=perlin_noise.svg, scale=0.02, octaves=1, persistence=0.5\n";
|
||||
std::cout << "Examples:\n";
|
||||
std::cout << " " << argv[0] << " 54321 noise1.svg 0.01\n";
|
||||
std::cout << " " << argv[0] << " 12345 octave_noise.svg 0.02 4 0.5\n";
|
||||
if (argc > 6) return 1;
|
||||
}
|
||||
|
||||
// Validate parameters
|
||||
if (scale <= 0) {
|
||||
std::cerr << "Error: Scale must be positive\n";
|
||||
return 1;
|
||||
}
|
||||
if (octaves < 1 || octaves > 10) {
|
||||
std::cerr << "Error: Octaves must be between 1 and 10\n";
|
||||
return 1;
|
||||
}
|
||||
if (persistence <= 0 || persistence > 1) {
|
||||
std::cerr << "Error: Persistence must be between 0 and 1\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Generating 256x256 Perlin noise visualization..." << std::endl;
|
||||
std::cout << "Parameters: seed=" << seed << ", scale=" << scale
|
||||
<< ", octaves=" << octaves << ", persistence=" << persistence << std::endl;
|
||||
|
||||
// Generate the noise visualization
|
||||
generate_noise_svg(output_filename, 256, scale, seed, octaves, persistence);
|
||||
|
||||
return 0;
|
||||
}
|
@ -36,11 +36,11 @@ struct BiomeProperties {
|
||||
// Biome name for debugging
|
||||
std::string_view name;
|
||||
|
||||
// Base terrain thresholds (0.0 - 1.0)
|
||||
double water_threshold;
|
||||
double ice_threshold;
|
||||
double sand_threshold;
|
||||
double land_threshold;
|
||||
// Base terrain ratios (0.0 - 1.0)
|
||||
double water_ratio;
|
||||
double ice_ratio;
|
||||
double sand_ratio;
|
||||
double land_ratio;
|
||||
|
||||
// Noise parameters for base terrain
|
||||
int base_octaves = 3;
|
||||
|
@ -11,74 +11,74 @@ constexpr BiomeProperties biome_properties[] = {
|
||||
{
|
||||
.name = "Snowy Peeks",
|
||||
|
||||
.water_threshold = .05,
|
||||
.ice_threshold = .15,
|
||||
.sand_threshold = .1,
|
||||
.land_threshold = .2,
|
||||
.water_ratio = .05,
|
||||
.ice_ratio = .15,
|
||||
.sand_ratio = .1,
|
||||
.land_ratio = .2,
|
||||
},
|
||||
// Snowy Plains (Cold & Moderate)
|
||||
{
|
||||
.name = "Snowy Plains",
|
||||
.water_threshold = .05,
|
||||
.ice_threshold = .25,
|
||||
.sand_threshold = .1,
|
||||
.land_threshold = .4,
|
||||
.water_ratio = .05,
|
||||
.ice_ratio = .25,
|
||||
.sand_ratio = .1,
|
||||
.land_ratio = .4,
|
||||
},
|
||||
// Frozen Ocean (Cold & Wet)
|
||||
{
|
||||
.name = "Frozen Ocean",
|
||||
.water_threshold = .3,
|
||||
.ice_threshold = .4,
|
||||
.sand_threshold = .25,
|
||||
.land_threshold = .05,
|
||||
.water_ratio = .3,
|
||||
.ice_ratio = .4,
|
||||
.sand_ratio = .25,
|
||||
.land_ratio = .05,
|
||||
},
|
||||
// Plains (Temperate & Dry)
|
||||
{
|
||||
.name = "Plains",
|
||||
.water_threshold = .1,
|
||||
.ice_threshold = .0,
|
||||
.sand_threshold = .05,
|
||||
.land_threshold = .65,
|
||||
.water_ratio = .1,
|
||||
.ice_ratio = .0,
|
||||
.sand_ratio = .05,
|
||||
.land_ratio = .65,
|
||||
},
|
||||
// Forest (Temperate & Moderate)
|
||||
{
|
||||
.name = "Forest",
|
||||
.water_threshold = .2,
|
||||
.ice_threshold = .0,
|
||||
.sand_threshold = .1,
|
||||
.land_threshold = .5,
|
||||
.water_ratio = .2,
|
||||
.ice_ratio = .0,
|
||||
.sand_ratio = .1,
|
||||
.land_ratio = .5,
|
||||
},
|
||||
// Ocean (Temperate & Wet)
|
||||
{
|
||||
.name = "Ocean",
|
||||
.water_threshold = .7,
|
||||
.ice_threshold = .0,
|
||||
.sand_threshold = .2,
|
||||
.land_threshold = .1,
|
||||
.water_ratio = .7,
|
||||
.ice_ratio = .0,
|
||||
.sand_ratio = .2,
|
||||
.land_ratio = .1,
|
||||
},
|
||||
// Desert (Hot & Dry)
|
||||
{
|
||||
.name = "Desert",
|
||||
.water_threshold = .0,
|
||||
.ice_threshold = .0,
|
||||
.sand_threshold = .75,
|
||||
.land_threshold = .05,
|
||||
.water_ratio = .0,
|
||||
.ice_ratio = .0,
|
||||
.sand_ratio = .75,
|
||||
.land_ratio = .05,
|
||||
},
|
||||
// Savanna (Hot & Moderate)
|
||||
{
|
||||
.name = "Savanna",
|
||||
.water_threshold = .2,
|
||||
.ice_threshold = .0,
|
||||
.sand_threshold = .1,
|
||||
.land_threshold = .5,
|
||||
.water_ratio = .2,
|
||||
.ice_ratio = .0,
|
||||
.sand_ratio = .1,
|
||||
.land_ratio = .5,
|
||||
},
|
||||
// Luke Ocean (Hot & Wet)
|
||||
{
|
||||
.name = "Luke Ocean",
|
||||
.water_threshold = .8,
|
||||
.ice_threshold = .0,
|
||||
.sand_threshold = .2,
|
||||
.land_threshold = .0,
|
||||
.water_ratio = .8,
|
||||
.ice_ratio = .0,
|
||||
.sand_ratio = .2,
|
||||
.land_ratio = .0,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -133,19 +133,19 @@ std::pair<double, double> TerrainGenerator::get_climate(
|
||||
BaseTileType TerrainGenerator::determine_base_type(
|
||||
double noise_value, const BiomeProperties &properties
|
||||
) const {
|
||||
const std::pair<BaseTileType, double> thresholds[] = {
|
||||
{BaseTileType::Water, properties.water_threshold},
|
||||
{BaseTileType::Ice, properties.ice_threshold },
|
||||
{BaseTileType::Sand, properties.sand_threshold },
|
||||
{BaseTileType::Land, properties.land_threshold },
|
||||
{BaseTileType::Mountain, 1.0 },
|
||||
const std::pair<BaseTileType, double> ratios[] = {
|
||||
{BaseTileType::Water, properties.water_ratio},
|
||||
{BaseTileType::Ice, properties.ice_ratio },
|
||||
{BaseTileType::Sand, properties.sand_ratio },
|
||||
{BaseTileType::Land, properties.land_ratio },
|
||||
{BaseTileType::Mountain, 1.0 },
|
||||
};
|
||||
|
||||
for (const auto &[type, threshold] : thresholds) {
|
||||
if (noise_value < threshold) {
|
||||
for (const auto &[type, ratio] : ratios) {
|
||||
if (noise_value < ratio) {
|
||||
return type;
|
||||
}
|
||||
noise_value -= threshold; // Adjust noise value for next type
|
||||
noise_value -= ratio; // Adjust noise value for next type
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
|
Loading…
x
Reference in New Issue
Block a user