-
Notifications
You must be signed in to change notification settings - Fork 1
/
genball.c
195 lines (151 loc) · 5.05 KB
/
genball.c
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// A miniature ray tracer to generate rotation frames for the ball sprite.
// One frame for each rotation around Z to the left, center, and right.
//
// Rotation around X is achieved with color cycling. There are 14 colors
// per sprite, 7 red and 7 white. Shift each of this colors along by one
// to give the illusion of forwards rotation.
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define kBallEdge 0x20
#define kSpriteWidth 0x10
#define kNumColors 0xE
#define kNumSpritesPerFrame 4
#define kNumFrameAngles 8
#define kNumFrames (kNumFrameAngles * 2 + 1)
#define kMaxAngle 1.1
#define kAngleStep (kMaxAngle / kNumFrameAngles)
typedef struct {
float v[2];
} vec2;
typedef struct {
float v[3];
} vec3;
void make_colors(int colors[kNumFrames][kBallEdge][kBallEdge]);
vec2 tex_center_norm(int spr_x, int spr_y);
vec2 tex_sphere_map(vec2 uv, bool* inside_ball);
vec2 tex_rotate(vec2 uv, float angle);
void make_sprites(int colors[kNumFrames][kBallEdge][kBallEdge]);
float checker_pattern(float x, float y);
float fraction(float n);
int main() {
static int colors[kNumFrames][kBallEdge][kBallEdge];
make_colors(colors);
make_sprites(colors);
}
void make_colors(int colors[kNumFrames][kBallEdge][kBallEdge]) {
for (int frame = 0; frame < kNumFrames; ++ frame) {
float angle = (kAngleStep * frame) + (M_PI / 2) - kMaxAngle;
for (int spr_y = 0; spr_y < kBallEdge; ++ spr_y) {
for (int spr_x = 0; spr_x < kBallEdge; ++ spr_x) {
bool inside_ball = false;
vec2 tex_uv = tex_center_norm(spr_x, spr_y);
tex_uv = tex_sphere_map(tex_uv, &inside_ball);
tex_uv = tex_rotate(tex_uv, angle);
int color = 0;
if (inside_ball) {
float checker = checker_pattern(tex_uv.v[0], tex_uv.v[1]);
color = 1 + (int)floorf(checker * kNumColors);
}
colors[frame][spr_y][spr_x] = color;
}
}
}
}
vec2 tex_center_norm(int spr_x,
int spr_y) {
return (vec2) {
1.0f - (((spr_y * 2) + 1) / (float)kBallEdge),
(((spr_x * 2) + 1) / (float)kBallEdge) - 1.0f,
};
}
float solve_one_root(float a,
float b,
float c) {
float d = (b * b) - (4 * a * c);
float x1 = (-b + sqrtf(d)) / (2 * a);
float x2 = (-b - sqrtf(d)) / (2 * a);
return x1 < x2 ? x1 : x2;
}
vec2 tex_sphere_map(vec2 uv,
bool* inside_ball) {
float radius_sqr =
(uv.v[0] * uv.v[0]) +
(uv.v[1] * uv.v[1]);
*inside_ball = (radius_sqr < 1.0f);
vec2 sph_uv = uv;//{0};
if (*inside_ball) {
float fov = M_PI / 4.0f;
vec3 cam = {0.0f, 0.0f, -1.0f / sin(fov / 2.0f)};
float near_z = 1.0f;
float near_w = tan(fov / 2) * near_z;
vec3 ray = {uv.v[0] * near_w, uv.v[1] * near_w, near_z};
float normalize = 0.0f;
for (int i = 0; i < 3; ++ i) {
normalize += ray.v[i] * ray.v[i];
}
normalize = sqrtf(normalize);
for (int i = 0; i < 3; ++ i) {
ray.v[i] /= normalize;
}
float quad_a = 1.0f;
float quad_b = 2 * ray.v[2] * cam.v[2];
float quad_c = (cam.v[2] * cam.v[2]) - 1.0f;
float isec_dist = solve_one_root(quad_a, quad_b, quad_c);
vec3 isec;
for (int i = 0; i < 3; ++ i) {
isec.v[i] = cam.v[i] + ray.v[i] * isec_dist;
}
sph_uv.v[0] = 3.0f * (0.5f + atan2f(isec.v[2], isec.v[0]) / M_PI);
sph_uv.v[1] = -3.0f * asinf(isec.v[1]) / M_PI;
}
return sph_uv;
}
vec2 tex_rotate(vec2 uv,
float angle) {
return (vec2){
(uv.v[0] * cosf(angle)) - (uv.v[1] * sinf(angle)),
(uv.v[1] * cosf(angle)) + (uv.v[0] * sinf(angle))
};
}
float checker_pattern(float x,
float y) {
float x_frac = fraction(x);
return fraction(y + (x_frac <= 0.5f ? 0.0f : 0.5f));
}
float fraction(float n) {
return n - floorf(n);
}
void make_sprites(int colors[kNumFrames][kBallEdge][kBallEdge]) {
printf("#include <exec/types.h>\n\n");
printf("#define kBallNumAngles %d\n\n", kNumFrameAngles);
printf("static UWORD ball_sprs[%d][%d][0x%X + 2][2] __chip = {\n",
kNumFrames, kNumSpritesPerFrame, kBallEdge);
for (int frame = 0; frame < kNumFrames; ++ frame) {
printf(" {\n");
for (int sprite = 0; sprite < kNumSpritesPerFrame; ++ sprite) {
printf(" {\n");
printf(" {0x0000, 0x0000},\n");
for (int y = 0; y < kBallEdge; ++ y) {
int plane_start = (sprite & 1) ? 2 : 0;
int x_start = (sprite < 2) ? 0 : kSpriteWidth;
printf(" {");
for (int plane = plane_start; plane <= (plane_start + 1); ++ plane) {
int plane_mask = 1 << plane;
unsigned short color_word = 0;
for (int bit = 0; bit < kSpriteWidth; ++ bit) {
int x = x_start + (kSpriteWidth - 1 - bit);
color_word |= ((colors[frame][y][x] & plane_mask) ? 1 : 0) << bit;
}
printf("%s0x%04hX", (plane > plane_start ? ", " : ""), color_word);
}
printf("},\n");
}
printf(" {0x0000, 0x0000},\n");
printf(" },\n");
}
printf(" },\n");
}
printf("};\n");
}