-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.cpp
More file actions
202 lines (160 loc) · 7.48 KB
/
Copy pathclient.cpp
File metadata and controls
202 lines (160 loc) · 7.48 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
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
196
197
198
199
200
201
202
//
// Created by bleepz on 2026-08-22.
//
//
// client.cpp — C++ render client for c-brain
//
#include "client.hpp"
#include "sokol/sokol_gfx.h"
#include "prim.h"
#include <cmath>
#include <cstring>
#include <cstdint>
/* ═══════════════════════════════════════════════════════════════════════
World geometry — regular tetrahedron
The tetrahedron bounds the player to the world.
Top vertex = WORLD_TOP
Base triangle = WORLD_BOTTOM
Centerfold cross-section (mid-height) = flat play plane
Origin is NOT at 0.0 — offset to WORLD_CENTERFOLD so world floats
ride the precision boundary there rather than fighting subnormals.
The float barely moves per player step; it oscillates at that level
and doesn't accumulate error. Integer coords lock everything stable.
Upgrade path:
Tetrahedron → flat "instance plane" (current)
Octahedron → full space above and below centerfold
═══════════════════════════════════════════════════════════════════════ */
static constexpr float TETRA_EDGE = 1024.0f;
static constexpr float TETRA_HEIGHT = TETRA_EDGE * 0.81649658f;
static constexpr float WORLD_CENTERFOLD = 1.0f;
static constexpr float WORLD_TOP = WORLD_CENTERFOLD + TETRA_HEIGHT * 0.5f;
static constexpr float WORLD_BOTTOM = WORLD_CENTERFOLD - TETRA_HEIGHT * 0.5f;
static constexpr float WORLD_RADIUS = TETRA_EDGE * 0.28867513f;
static constexpr float CELL_SIZE = 1.0f;
static constexpr int32_t WORLD_INT_W = 1000;
/* suppress unused until octahedron upgrade */
static_assert(WORLD_TOP > WORLD_CENTERFOLD, "tetra top above centerfold");
static_assert(WORLD_BOTTOM < WORLD_CENTERFOLD, "tetra bottom below centerfold");
/* ── Scene shader — GLSL 330 (SOKOL_GLCORE) ─────────────────────────── */
static const char* s_vs_src =
"#version 330\n"
"layout(location=0) in vec2 a_pos;\n"
"layout(location=1) in vec2 a_nrm;\n"
"uniform vec2 u_centerfold;\n"
"uniform float u_inv_radius;\n"
"out vec4 v_color;\n"
"void main() {\n"
" vec2 ndc = (a_pos - u_centerfold) * u_inv_radius;\n"
" gl_Position = vec4(ndc, 0.0, 1.0);\n"
" v_color = vec4(a_nrm * 0.5 + 0.5, 0.8, 1.0);\n"
"}\n";
static const char* s_fs_src =
"#version 330\n"
"in vec4 v_color;\n"
"out vec4 frag_color;\n"
"void main() {\n"
" frag_color = v_color;\n"
"}\n";
/* ── Static client state ─────────────────────────────────────────────── */
static constexpr int CLIENT_VBO_FLOATS = 65536;
static sg_pipeline s_pip;
static sg_buffer s_vbuf;
static sg_bindings s_bind;
static int32_t s_player_coord = 0;
static bool s_ready = false;
static float s_frame_buf[CLIENT_VBO_FLOATS];
/* ── Uniforms ────────────────────────────────────────────────────────── */
struct SceneUniforms {
float centerfold[2];
float inv_radius;
};
static SceneUniforms s_uniforms = {
.centerfold = { WORLD_CENTERFOLD, WORLD_CENTERFOLD },
.inv_radius = 1.0f / WORLD_RADIUS,
};
/* ── Public API ──────────────────────────────────────────────────────── */
extern "C" void client_init(void) {
/* Shader — name the desc, then pass its address */
sg_shader_desc shd_desc = {};
shd_desc.vertex_func.source = s_vs_src;
shd_desc.fragment_func.source = s_fs_src;
shd_desc.attrs[0].glsl_name = "a_pos";
shd_desc.attrs[1].glsl_name = "a_nrm";
shd_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX;
shd_desc.uniform_blocks[0].size = sizeof(SceneUniforms);
shd_desc.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_FLOAT2;
shd_desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "u_centerfold";
shd_desc.uniform_blocks[0].glsl_uniforms[1].type = SG_UNIFORMTYPE_FLOAT;
shd_desc.uniform_blocks[0].glsl_uniforms[1].glsl_name = "u_inv_radius";
sg_shader shd = sg_make_shader(&shd_desc);
/* Pipeline --------------------------------------------------------- */
sg_pipeline_desc pip_desc = {};
pip_desc.shader = shd;
pip_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2;
pip_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
pip_desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES;
pip_desc.label = "client_pipeline";
s_pip = sg_make_pipeline(&pip_desc);
/* Stream VBO ------------------------------------------------------- */
sg_buffer_desc buf_desc = {};
buf_desc.size = CLIENT_VBO_FLOATS * sizeof(float);
buf_desc.usage.stream_update = true;
buf_desc.label = "client_vbo";
s_vbuf = sg_make_buffer(&buf_desc);
/* Bindings --------------------------------------------------------- */
memset(&s_bind, 0, sizeof(s_bind));
s_bind.vertex_buffers[0] = s_vbuf;
sg_destroy_shader(shd);
s_ready = true;
}
extern "C" void client_shutdown(void) {
if (!s_ready) return;
sg_destroy_pipeline(s_pip);
sg_destroy_buffer(s_vbuf);
s_ready = false;
}
extern "C" void client_set_player_coord(int32_t coord) {
s_player_coord = coord;
}
extern "C" void client_on_renderable(BigT window, SimPrimitive* live,
int count, sg_buffer /*vbo*/) {
(void)window;
(void)vbo;
if (!s_ready || count == 0) return;
int32_t px = s_player_coord % WORLD_INT_W;
int32_t py = s_player_coord / WORLD_INT_W;
int frame_floats = 0;
int total_verts = 0;
for (int i = 0; i < count; i++) {
int32_t ex = live[i].coord % WORLD_INT_W;
int32_t ey = live[i].coord / WORLD_INT_W;
float dx = (float)(ex - px) * CELL_SIZE + WORLD_CENTERFOLD;
float dy = (float)(ey - py) * CELL_SIZE + WORLD_CENTERFOLD;
ShapeDesc sd = {};
sd.shell.radius = live[i].shape;
sd.shell.scale = 1.0f;
sd.shell.x = dx;
sd.shell.y = dy;
sd.n_sequences = 8;
sd.joint_threshold = 0.001f;
PrimResult pr = prim_generate(&sd);
int floats_this = pr.vert_count * 4;
if (pr.floats && frame_floats + floats_this <= CLIENT_VBO_FLOATS) {
memcpy(s_frame_buf + frame_floats, pr.floats,
(size_t)floats_this * sizeof(float));
frame_floats += floats_this;
total_verts += pr.vert_count;
}
prim_free(&pr);
}
if (total_verts == 0) return;
/* Upload ----------------------------------------------------------- */
sg_range update_range = { s_frame_buf, (size_t)frame_floats * sizeof(float) };
sg_update_buffer(s_vbuf, &update_range);
/* Draw ------------------------------------------------------------- */
sg_range uni_range = { &s_uniforms, sizeof(s_uniforms) };
sg_apply_pipeline(s_pip);
sg_apply_bindings(&s_bind);
sg_apply_uniforms(0, &uni_range);
sg_draw(0, total_verts, 1);
}