-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathhello_tilemap.cpp
More file actions
135 lines (116 loc) · 5.05 KB
/
Copy pathhello_tilemap.cpp
File metadata and controls
135 lines (116 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/// hello_tilemap.cpp
/// Demonstrates the palette-indexed tile-map mode of IndirectTexture: two handcrafted
/// tiles (layers) arranged in a 4×3 checkerboard via setMap + setCell.
/// Layer 0 — white circle on a black background
/// Layer 1 — red→yellow diagonal ordered-dithered gradient (2×2 Bayer matrix)
/// All tiles store palette indices; the shared palette resolves the colors.
#include <nothofagus.h>
#include <cstdint>
#include <vector>
#include <cmath>
// Palette layout (kept tiny — only 5 entries used).
namespace Pal
{
constexpr std::uint8_t Transparent = 0;
constexpr std::uint8_t Black = 1; // circle background
constexpr std::uint8_t White = 2; // circle foreground
constexpr std::uint8_t Red = 3; // dither low
constexpr std::uint8_t Yellow = 4; // dither high
}
// ---------------------------------------------------------------------------
// Tile 0: white filled circle on black background
// ---------------------------------------------------------------------------
static std::vector<std::uint8_t> makeCircleTile(glm::ivec2 tileSize)
{
const int w = tileSize.x, h = tileSize.y;
const float cx = (w - 1) * 0.5f;
const float cy = (h - 1) * 0.5f;
const float r2 = (w * 0.5f - 0.5f) * (w * 0.5f - 0.5f);
std::vector<std::uint8_t> data(static_cast<std::size_t>(w * h));
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const float dx = x - cx, dy = y - cy;
const bool inside = (dx * dx + dy * dy <= r2);
data[static_cast<std::size_t>(y * w + x)] = inside ? Pal::White : Pal::Black;
}
}
return data;
}
// ---------------------------------------------------------------------------
// Tile 1: red→yellow diagonal gradient dithered with a 2×2 Bayer matrix.
// Per pixel the Bayer threshold picks Red (low) or Yellow (high).
// ---------------------------------------------------------------------------
static std::vector<std::uint8_t> makeDitherGradientTile(glm::ivec2 tileSize)
{
const int w = tileSize.x, h = tileSize.y;
const float maxD = static_cast<float>((w - 1) + (h - 1));
// 2×2 Bayer ordered-dither thresholds
static constexpr float kBayer[2][2] = { { 0.25f, 0.75f }, { 0.75f, 0.25f } };
std::vector<std::uint8_t> data(static_cast<std::size_t>(w * h));
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const float d = static_cast<float>(x + y) / maxD;
const float threshold = kBayer[y % 2][x % 2];
const bool isYellow = (d > threshold);
data[static_cast<std::size_t>(y * w + x)] = isYellow ? Pal::Yellow : Pal::Red;
}
}
return data;
}
// ---------------------------------------------------------------------------
int main()
{
constexpr glm::ivec2 tileSize {8, 8};
constexpr glm::ivec2 mapSize {4, 3}; // 4 columns × 3 rows (horizontal layout)
constexpr int pixelScale = 16; // 16× zoom → 512×384 window
Nothofagus::Canvas canvas(
{ mapSize.x * tileSize.x, mapSize.y * tileSize.y },
"Hello TileMap",
{ 0.0f, 0.0f, 0.0f },
pixelScale
);
// Build the palette-indexed tile map.
// Two unique tiles are stored as two layers of an IndirectTexture; setMap opts
// the texture into tile-map rendering with a 4×3 cell grid that picks per-cell
// which layer to draw.
constexpr std::size_t tileCount = 2;
Nothofagus::IndirectTexture tileMap(tileSize, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), tileCount);
tileMap.setPallete(Nothofagus::ColorPallete{
{0.0f, 0.0f, 0.0f, 0.0f}, // 0 transparent
{0.0f, 0.0f, 0.0f, 1.0f}, // 1 black
{1.0f, 1.0f, 1.0f, 1.0f}, // 2 white
{1.0f, 0.0f, 0.0f, 1.0f}, // 3 red
{1.0f, 1.0f, 0.0f, 1.0f}, // 4 yellow
});
auto circlePx = makeCircleTile(tileSize);
tileMap.setPixels(std::span<const std::uint8_t>(circlePx), 0);
auto ditherPx = makeDitherGradientTile(tileSize);
tileMap.setPixels(std::span<const std::uint8_t>(ditherPx), 1);
tileMap.setMap(mapSize);
// Checkerboard: (col + row) % 2 selects layer 0 or 1
for (int row = 0; row < mapSize.y; ++row)
for (int col = 0; col < mapSize.x; ++col)
tileMap.setCell(col, row, static_cast<std::uint8_t>((col + row) % 2));
Nothofagus::TextureId tileMapTexId = canvas.addTexture(tileMap);
// One bellota centered on the canvas at scale 1:1.
// The mesh is already sized to the texture dimensions (mapSize * tileSize),
// so scale = 1 fills the canvas exactly.
const glm::vec2 center{
static_cast<float>(mapSize.x * tileSize.x) * 0.5f,
static_cast<float>(mapSize.y * tileSize.y) * 0.5f
};
canvas.addBellota({
Nothofagus::Transform(center),
tileMapTexId
});
Nothofagus::Controller controller;
controller.registerAction(
{ Nothofagus::Key::ESCAPE, Nothofagus::DiscreteTrigger::Press },
[&]() { canvas.close(); });
canvas.run([](float) {}, controller);
return 0;
}