Far out stays steady: the grid's lines worked out from small numbers (its square's middle a whole number of kilometres from the world's, found in doubles; where on it from there), so far from the origin they no longer turn grainy and shake; the sun's shadow maps snap to their texels in doubles; packaging reads the version from project.json

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-21 17:41:25 +02:00
commit 1ee38c966c
8 changed files with 67 additions and 55 deletions

View file

@ -11,6 +11,7 @@ Signed with ~/.local/share/Nexoniarz/Fry3DX-signing/release.keystore (made the f
or the key in $FRY_KEYSTORE / $FRY_KEYSTORE_PASS. Keep it safe and keep using it: Android only or the key in $FRY_KEYSTORE / $FRY_KEYSTORE_PASS. Keep it safe and keep using it: Android only
updates an app signed with the key it was installed with. updates an app signed with the key it was installed with.
""" """
import json
import os import os
import secrets import secrets
import shutil import shutil
@ -30,11 +31,8 @@ def run(*cmd, **kw):
def app_version(): def app_version():
with open(os.path.join(ROOT, "CMakeLists.txt")) as f: with open(os.path.join(ROOT, "project.json")) as f: # (the version and the rest live there)
for line in f: return json.load(f)["version"]
if line.startswith("project(Fry3DX VERSION "):
return line.split()[2]
return "0"
def main(): def main():

View file

@ -211,10 +211,18 @@ void Renderer::drawSprite(const glm::vec3& at, float size, const glm::vec4& colo
list.push_back({glm::vec4(at, size * 0.5f), color, glm::vec4(0, 0, 0, rotation), glm::vec4(0, 0, 0, tex)}); list.push_back({glm::vec4(at, size * 0.5f), color, glm::vec4(0, 0, 0, rotation), glm::vec4(0, 0, 0, tex)});
} }
void Renderer::drawGrid(const glm::vec3& centre, const glm::vec3& u, const glm::vec3& v, float metre, const glm::vec3& look, float reach) { void Renderer::drawGrid(const glm::dvec3& under, const glm::vec3& u, const glm::vec3& v, float metre, bool dark, float reach) {
if (packet_.sprites.size() >= 8192) return; if (packet_.sprites.size() >= 8192) return;
// Its middle a whole number of kilometres along u and v (so the lines are worked out from
// small numbers, sprite.frag): a square round it out to `reach` past the eye.
const double km = 1000.0 * double(metre);
const glm::dvec3 du(u), dv(v);
const double cu = std::round(glm::dot(under, du) / km) * km, cv = std::round(glm::dot(under, dv) / km) * km;
const glm::dvec3 centre = du * cu + dv * cv;
const glm::vec2 eye(float(glm::dot(under, du) - cu), float(glm::dot(under, dv) - cv));
const float half = reach + glm::length(eye);
// (colour.a < 0 marks it; sprite.vert / sprite.frag) // (colour.a < 0 marks it; sprite.vert / sprite.frag)
packet_.sprites.push_back({glm::vec4(centre, reach), glm::vec4(look, -1.0f), glm::vec4(u, metre), glm::vec4(v, 0.0f)}); packet_.sprites.push_back({glm::vec4(glm::vec3(centre), half), glm::vec4(eye, 0.0f, dark ? -1.0f : -2.0f), glm::vec4(u, metre), glm::vec4(v, reach)});
} }
void Renderer::drawStreak(const glm::vec3& from, const glm::vec3& to, float width, const glm::vec4& color, TextureId texture, bool additive, float widthTo) { void Renderer::drawStreak(const glm::vec3& from, const glm::vec3& to, float width, const glm::vec4& color, TextureId texture, bool additive, float widthTo) {
@ -453,11 +461,13 @@ void Renderer::buildPacket(uint32_t width, uint32_t height) {
for (const glm::vec3& q : corners) radius = std::max(radius, glm::length(q - center)); for (const glm::vec3& q : corners) radius = std::max(radius, glm::length(q - center));
radius = std::ceil(radius / 16.0f) * 16.0f; radius = std::ceil(radius / 16.0f) * 16.0f;
const float texel = 2.0f * radius / cascadeSize; const float texel = 2.0f * radius / cascadeSize;
glm::mat4 lightView = glm::lookAt(glm::vec3(0), -sunDir_, up); // (in doubles: far from the origin a float's steps are near a texel's, and it'd shimmer)
glm::vec4 lc = lightView * glm::vec4(center, 1); const glm::dmat4 lightView0 = glm::lookAt(glm::dvec3(0), -glm::dvec3(sunDir_), glm::dvec3(up));
glm::dvec4 lc = lightView0 * glm::dvec4(glm::dvec3(center), 1);
lc.x = std::floor(lc.x / texel) * texel; lc.x = std::floor(lc.x / texel) * texel;
lc.y = std::floor(lc.y / texel) * texel; lc.y = std::floor(lc.y / texel) * texel;
center = glm::vec3(glm::inverse(lightView) * lc); center = glm::vec3(glm::inverse(lightView0) * lc);
glm::mat4 lightView;
lightView = glm::lookAt(center + sunDir_ * 8000.0f, center, up); lightView = glm::lookAt(center + sunDir_ * 8000.0f, center, up);
frame.cascades[c] = glm::ortho(-radius, radius, -radius, radius, 1.0f, 16000.0f) * lightView; frame.cascades[c] = glm::ortho(-radius, radius, -radius, radius, 1.0f, 16000.0f) * lightView;
frame.splits[c] = f; frame.splits[c] = f;

View file

@ -101,11 +101,12 @@ public:
// A sprite stretched from `from` to `to`, `width` across (`widthTo` at `to`: < 0, the same), // A sprite stretched from `from` to `to`, `width` across (`widthTo` at `to`: < 0, the same),
// each end turned to face the eye as much as it can (rain; lines drawn in pieces meet exactly). // each end turned to face the eye as much as it can (rain; lines drawn in pieces meet exactly).
void drawStreak(const glm::vec3& from, const glm::vec3& to, float width, const glm::vec4& color, TextureId texture, bool additive = false, float widthTo = -1.0f); void drawStreak(const glm::vec3& from, const glm::vec3& to, float width, const glm::vec4& color, TextureId texture, bool additive = false, float widthTo = -1.0f);
// A grid of lines on the plane through `centre` along u and v (unit), drawn for each pixel: // A grid of lines on the plane through the origin along u and v (unit), drawn for each pixel:
// every 10^n metres (`metre`: units in one), each set showing by how far apart its lines are // every 10^n metres (`metre`: units in one), each set showing by how far apart its lines are
// on the screen, the axes coloured; look: the lines' shade (0 dark, 1 light), how faint, how // on the screen, the axes coloured; dark lines (on the sunny ground) or light ones (in the
// strong; fading out `reach` from the centre. // grey room); fading out `reach` from `under` (the point under the eye, as a double: far out,
void drawGrid(const glm::vec3& centre, const glm::vec3& u, const glm::vec3& v, float metre, const glm::vec3& look, float reach); // a float's too coarse for lines a pixel wide).
void drawGrid(const glm::dvec3& under, const glm::vec3& u, const glm::vec3& v, float metre, bool dark, float reach);
// skin: one matrix per joint (anim::skinMatrices) for skinned models; ignored otherwise. // skin: one matrix per joint (anim::skinMatrices) for skinned models; ignored otherwise.
// lighting: probe light for moving things (nullptr = lightmap or simple sun shading). // lighting: probe light for moving things (nullptr = lightmap or simple sun shading).
void drawModel(ModelId model, const glm::mat4& transform, const glm::vec4& tint = glm::vec4(1.0f), void drawModel(ModelId model, const glm::mat4& transform, const glm::vec4& tint = glm::vec4(1.0f),

View file

@ -368,7 +368,7 @@ uniform mat4 u_viewProj;
uniform vec4 u_posSize, u_right, u_up; // right.w: turn (radians) uniform vec4 u_posSize, u_right, u_up; // right.w: turn (radians)
uniform vec4 u_color; uniform vec4 u_color;
out vec2 v_uv; out vec2 v_uv;
out vec3 v_world; out vec2 v_rel; // (a grid: where on it, from its middle)
const vec2 kQuad[6] = vec2[](vec2(-1,-1), vec2(1,-1), vec2(1,1), vec2(-1,-1), vec2(1,1), vec2(-1,1)); const vec2 kQuad[6] = vec2[](vec2(-1,-1), vec2(1,-1), vec2(1,1), vec2(-1,-1), vec2(1,1), vec2(-1,1));
void main() { void main() {
vec2 q = kQuad[gl_VertexID]; vec2 q = kQuad[gl_VertexID];
@ -381,43 +381,46 @@ void main() {
world = end + (length(side) > 1e-9 ? normalize(side) : vec3(0.0, 0.0, 1.0)) * q.x * (q.y < 0.0 ? -u_posSize.w : u_right.w); world = end + (length(side) > 1e-9 ? normalize(side) : vec3(0.0, 0.0, 1.0)) * q.x * (q.y < 0.0 ? -u_posSize.w : u_right.w);
} }
v_uv = vec2(q.x * 0.5 + 0.5, 0.5 - q.y * 0.5); v_uv = vec2(q.x * 0.5 + 0.5, 0.5 - q.y * 0.5);
v_world = world; v_rel = u_color.a < 0.0 ? q * u_posSize.w : vec2(0.0);
gl_Position = u_viewProj * vec4(world, 1.0); gl_Position = u_viewProj * vec4(world, 1.0);
gl_Position.z = gl_Position.z * 2.0 - gl_Position.w; gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;
} }
)"; )";
const char* kSpriteFrag = R"( const char* kSpriteFrag = R"(
in vec2 v_uv; in vec2 v_uv;
in vec3 v_world; in vec2 v_rel;
uniform sampler2D u_albedo; uniform sampler2D u_albedo;
uniform vec4 u_color, u_posSize, u_right, u_up; uniform vec4 u_color, u_posSize, u_right, u_up;
out vec4 o_color; out vec4 o_color;
// A grid (a sprite with colour.a < 0), worked out for each pixel where it lies on its plane: // A grid (a sprite with colour.a < 0: -1 dark lines, -2 light ones), worked out for each pixel
// lines every 10^n metres (`metre`: units in one), the four sets from the finest that isn't // where it lies on its plane: lines every 10^n metres (`metre`: units in one), the four sets from
// crowded together here, each showing by how far apart its lines are on the screen (none where // the finest that isn't crowded together here, each showing by how far apart its lines are on the
// they crowd, stronger where they're far apart: nothing jumps as the view moves), about a pixel // screen (none where they crowd, stronger where they're far apart: nothing jumps as the view
// wide, smooth; the axes through the middle in the colour of the world's axis they run along. // moves), about a pixel wide, smooth; the axes through the middle in the colour of the world's
// look: x the lines' shade (0 dark, 1 light), y how faint, z how strong; reach: it fades out there. // axis they run along. All from `rel`, where it is from the square's middle (a whole number of
// kilometres from the world's: far out, the world's own numbers are too coarse for lines a
// pixel wide); `eye`: the point under the eye, from the middle; it fades out `reach` from there.
vec3 gridAxisColor(vec3 d) { vec3 gridAxisColor(vec3 d) {
d = abs(d); d = abs(d);
return d.x > 0.5 ? vec3(0.9, 0.25, 0.25) : d.y > 0.5 ? vec3(0.3, 0.75, 0.25) : vec3(0.25, 0.45, 0.95); return d.x > 0.5 ? vec3(0.9, 0.25, 0.25) : d.y > 0.5 ? vec3(0.3, 0.75, 0.25) : vec3(0.25, 0.45, 0.95);
} }
vec4 gridColor(vec3 w, vec3 centre, vec3 u, vec3 v, float metre, vec3 look, float reach) { vec4 gridColor(vec2 rel, vec3 centre, vec3 u, vec3 v, float metre, float kind, vec2 eye, float reach) {
vec2 c = vec2(dot(w, u), dot(w, v)); vec2 base = vec2(dot(centre, u), dot(centre, v)); // (the middle: whole kilometres)
vec2 fw = max(fwidth(c), vec2(1e-9)); vec2 fw = max(fwidth(rel), vec2(1e-9));
float finest = floor(log(min(fw.x, fw.y) * 5.0 / metre) / log(10.0)); // (10^this m: 5 pixels apart or more) float finest = floor(log(min(fw.x, fw.y) * 5.0 / metre) / log(10.0)); // (10^this m: 5 pixels apart or more)
vec3 look = kind < -1.5 ? vec3(1.0, 0.05, 0.16) : vec3(0.0, 0.14, 0.4); // (shade, how faint, how strong)
float lines = 0.0; float lines = 0.0;
for (int k = 0; k < 4; ++k) { for (int k = 0; k < 4; ++k) {
float s = metre * pow(10.0, finest + float(k)); float s = metre * pow(10.0, finest + float(k));
vec2 apart = s / fw; // (pixels between this set's lines, each way) vec2 apart = s / fw; // (pixels between this set's lines, each way)
vec2 off = abs(fract(c / s + 0.5) - 0.5) * apart; // (pixels to the nearest of them) vec2 off = abs(fract(fract(base / s) + rel / s + 0.5) - 0.5) * apart; // (pixels to the nearest of them)
vec2 strong = smoothstep(20.0, 160.0, apart); vec2 strong = smoothstep(20.0, 160.0, apart);
vec2 cover = clamp(0.5 * (1.0 + 0.6 * strong) + 0.5 - off, 0.0, 1.0); vec2 cover = clamp(0.5 * (1.0 + 0.6 * strong) + 0.5 - off, 0.0, 1.0);
vec2 a = cover * smoothstep(5.0, 20.0, apart) * mix(vec2(look.y), vec2(look.z), strong); vec2 a = cover * smoothstep(5.0, 20.0, apart) * mix(vec2(look.y), vec2(look.z), strong);
lines = max(lines, max(a.x, a.y)); lines = max(lines, max(a.x, a.y));
} }
vec2 axis = clamp(1.25 - abs(c) / fw, 0.0, 1.0) * 0.9; // (x: the line along v, y: along u) vec2 axis = clamp(1.25 - abs(base + rel) / fw, 0.0, 1.0) * 0.9; // (x: the line along v, y: along u)
float fade = smoothstep(reach, reach * 0.6, length(w - centre)); float fade = smoothstep(reach, reach * 0.6, length(rel - eye));
vec3 col = vec3(look.x) * lines; vec3 col = vec3(look.x) * lines;
float a = lines; float a = lines;
col = mix(col, gridAxisColor(v), axis.x), a = mix(a, 1.0, axis.x); col = mix(col, gridAxisColor(v), axis.x), a = mix(a, 1.0, axis.x);
@ -426,7 +429,7 @@ vec4 gridColor(vec3 w, vec3 centre, vec3 u, vec3 v, float metre, vec3 look, floa
} }
void main() { void main() {
if (u_color.a < 0.0) { if (u_color.a < 0.0) {
o_color = gridColor(v_world, u_posSize.xyz, normalize(u_right.xyz), normalize(u_up.xyz), u_right.w, u_color.rgb, u_posSize.w); o_color = gridColor(v_rel, u_posSize.xyz, normalize(u_right.xyz), normalize(u_up.xyz), u_right.w, u_color.a, u_color.xy, u_up.w);
return; return;
} }
vec4 t = texture(u_albedo, v_uv); vec4 t = texture(u_albedo, v_uv);

View file

@ -9,33 +9,36 @@ PUSH_BLOCK {
vec4 up; vec4 up;
} pc; } pc;
layout(location = 0) in vec2 v_uv; layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec3 v_world; layout(location = 1) in vec2 v_rel;
// A grid (a sprite with colour.a < 0), worked out for each pixel where it lies on its plane: // A grid (a sprite with colour.a < 0: -1 dark lines, -2 light ones), worked out for each pixel
// lines every 10^n metres (`metre`: units in one), the four sets from the finest that isn't // where it lies on its plane: lines every 10^n metres (`metre`: units in one), the four sets from
// crowded together here, each showing by how far apart its lines are on the screen (none where // the finest that isn't crowded together here, each showing by how far apart its lines are on the
// they crowd, stronger where they're far apart: nothing jumps as the view moves), about a pixel // screen (none where they crowd, stronger where they're far apart: nothing jumps as the view
// wide, smooth; the axes through the middle in the colour of the world's axis they run along. // moves), about a pixel wide, smooth; the axes through the middle in the colour of the world's
// look: x the lines' shade (0 dark, 1 light), y how faint, z how strong; reach: it fades out there. // axis they run along. All from `rel`, where it is from the square's middle (a whole number of
// kilometres from the world's: far out, the world's own numbers are too coarse for lines a
// pixel wide); `eye`: the point under the eye, from the middle; it fades out `reach` from there.
vec3 gridAxisColor(vec3 d) { vec3 gridAxisColor(vec3 d) {
d = abs(d); d = abs(d);
return d.x > 0.5 ? vec3(0.9, 0.25, 0.25) : d.y > 0.5 ? vec3(0.3, 0.75, 0.25) : vec3(0.25, 0.45, 0.95); return d.x > 0.5 ? vec3(0.9, 0.25, 0.25) : d.y > 0.5 ? vec3(0.3, 0.75, 0.25) : vec3(0.25, 0.45, 0.95);
} }
vec4 gridColor(vec3 w, vec3 centre, vec3 u, vec3 v, float metre, vec3 look, float reach) { vec4 gridColor(vec2 rel, vec3 centre, vec3 u, vec3 v, float metre, float kind, vec2 eye, float reach) {
vec2 c = vec2(dot(w, u), dot(w, v)); vec2 base = vec2(dot(centre, u), dot(centre, v)); // (the middle: whole kilometres)
vec2 fw = max(fwidth(c), vec2(1e-9)); vec2 fw = max(fwidth(rel), vec2(1e-9));
float finest = floor(log(min(fw.x, fw.y) * 5.0 / metre) / log(10.0)); // (10^this m: 5 pixels apart or more) float finest = floor(log(min(fw.x, fw.y) * 5.0 / metre) / log(10.0)); // (10^this m: 5 pixels apart or more)
vec3 look = kind < -1.5 ? vec3(1.0, 0.05, 0.16) : vec3(0.0, 0.14, 0.4); // (shade, how faint, how strong)
float lines = 0.0; float lines = 0.0;
for (int k = 0; k < 4; ++k) { for (int k = 0; k < 4; ++k) {
float s = metre * pow(10.0, finest + float(k)); float s = metre * pow(10.0, finest + float(k));
vec2 apart = s / fw; // (pixels between this set's lines, each way) vec2 apart = s / fw; // (pixels between this set's lines, each way)
vec2 off = abs(fract(c / s + 0.5) - 0.5) * apart; // (pixels to the nearest of them) vec2 off = abs(fract(fract(base / s) + rel / s + 0.5) - 0.5) * apart; // (pixels to the nearest of them)
vec2 strong = smoothstep(20.0, 160.0, apart); vec2 strong = smoothstep(20.0, 160.0, apart);
vec2 cover = clamp(0.5 * (1.0 + 0.6 * strong) + 0.5 - off, 0.0, 1.0); vec2 cover = clamp(0.5 * (1.0 + 0.6 * strong) + 0.5 - off, 0.0, 1.0);
vec2 a = cover * smoothstep(5.0, 20.0, apart) * mix(vec2(look.y), vec2(look.z), strong); vec2 a = cover * smoothstep(5.0, 20.0, apart) * mix(vec2(look.y), vec2(look.z), strong);
lines = max(lines, max(a.x, a.y)); lines = max(lines, max(a.x, a.y));
} }
vec2 axis = clamp(1.25 - abs(c) / fw, 0.0, 1.0) * 0.9; // (x: the line along v, y: along u) vec2 axis = clamp(1.25 - abs(base + rel) / fw, 0.0, 1.0) * 0.9; // (x: the line along v, y: along u)
float fade = smoothstep(reach, reach * 0.6, length(w - centre)); float fade = smoothstep(reach, reach * 0.6, length(rel - eye));
vec3 col = vec3(look.x) * lines; vec3 col = vec3(look.x) * lines;
float a = lines; float a = lines;
col = mix(col, gridAxisColor(v), axis.x), a = mix(a, 1.0, axis.x); col = mix(col, gridAxisColor(v), axis.x), a = mix(a, 1.0, axis.x);
@ -45,8 +48,8 @@ vec4 gridColor(vec3 w, vec3 centre, vec3 u, vec3 v, float metre, vec3 look, floa
layout(location = 0) out vec4 o_color; layout(location = 0) out vec4 o_color;
void main() { void main() {
if (pc.color.a < 0.0) { // a grid: right.w the units in a metre, color.rgb its look, posSize.w how far it reaches if (pc.color.a < 0.0) { // a grid: right.w the units in a metre, up.w how far it reaches, color.xy where the eye is over it
o_color = gridColor(v_world, pc.posSize.xyz, normalize(pc.right.xyz), normalize(pc.up.xyz), pc.right.w, pc.color.rgb, pc.posSize.w); o_color = gridColor(v_rel, pc.posSize.xyz, normalize(pc.right.xyz), normalize(pc.up.xyz), pc.right.w, pc.color.a, pc.color.xy, pc.up.w);
return; return;
} }
vec4 t = sampleTex(floatBitsToUint(pc.up.w), v_uv); vec4 t = sampleTex(floatBitsToUint(pc.up.w), v_uv);

View file

@ -9,7 +9,7 @@ PUSH_BLOCK {
vec4 up; // camera up, w = texture index (bits) vec4 up; // camera up, w = texture index (bits)
} pc; } pc;
layout(location = 0) out vec2 v_uv; layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec3 v_world; layout(location = 1) out vec2 v_rel; // (a grid: where on it, from its middle)
const vec2 kQuad[6] = vec2[](vec2(-1,-1), vec2(1,-1), vec2(1,1), vec2(-1,-1), vec2(1,1), vec2(-1,1)); const vec2 kQuad[6] = vec2[](vec2(-1,-1), vec2(1,-1), vec2(1,1), vec2(-1,-1), vec2(1,1), vec2(-1,1));
void main() { void main() {
vec2 q = kQuad[VERTEX_ID]; vec2 q = kQuad[VERTEX_ID];
@ -23,6 +23,6 @@ void main() {
} }
v_uv = q * 0.5 + 0.5; v_uv = q * 0.5 + 0.5;
v_uv.y = 1.0 - v_uv.y; v_uv.y = 1.0 - v_uv.y;
v_world = world; v_rel = pc.color.a < 0.0 ? q * pc.posSize.w : vec2(0.0);
gl_Position = pc.viewProj * vec4(world, 1.0); gl_Position = pc.viewProj * vec4(world, 1.0);
} }

View file

@ -1412,9 +1412,8 @@ auto App::draw3D(keel::Renderer& r) -> void {
const glm::vec3 u = n.z > 0.5f ? glm::vec3(1, 0, 0) : n.x > 0.5f ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0); const glm::vec3 u = n.z > 0.5f ? glm::vec3(1, 0, 0) : n.x > 0.5f ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0);
const glm::vec3 v = glm::cross(n, u); const glm::vec3 v = glm::cross(n, u);
const float reach = nearFar_.y / kUnits * 0.9f; const float reach = nearFar_.y / kUnits * 0.9f;
const glm::vec3 under = eye_ - n * glm::dot(eye_, n); // (the grid's plane goes through the origin) const glm::dvec3 eye(eye_), dn(n), under = (eye - dn * glm::dot(eye, dn)) * double(kUnits); // (the grid's plane goes through the origin)
const glm::vec3 look = rendered ? glm::vec3(0.0f, 0.14f, 0.4f) : glm::vec3(1.0f, 0.05f, 0.16f); // (dark lines on the sunny ground, light ones in the grey room) r.drawGrid(under, u, v, kUnits, rendered, reach * kUnits); // (dark lines on the sunny ground, light ones in the grey room)
r.drawGrid(under * kUnits, u, v, kUnits, look, reach * kUnits);
} }
if (tab_ == Tab::Print) drawPrintBed(r, light); if (tab_ == Tab::Print) drawPrintBed(r, light);
} }

View file

@ -12,6 +12,7 @@
Each unpacks to one Fry3DX/ folder: the program, its content (engine/, Fry3DX/; on a Mac inside Each unpacks to one Fry3DX/ folder: the program, its content (engine/, Fry3DX/; on a Mac inside
the app), LICENSE and a README.txt. (The Android APK: android/build_apk.py.) the app), LICENSE and a README.txt. (The Android APK: android/build_apk.py.)
""" """
import json
import os import os
import shutil import shutil
import subprocess import subprocess
@ -36,11 +37,8 @@ Open source under the Apache License 2.0 (LICENSE). Sky pictures: Poly Haven, CC
def version(): def version():
with open(os.path.join(ROOT, "CMakeLists.txt")) as f: with open(os.path.join(ROOT, "project.json")) as f: # (the version and the rest live there)
for line in f: return json.load(f)["version"]
if line.startswith("project(Fry3DX VERSION "):
return line.split()[2]
return "0"
PC_NEEDS = "a graphics card with Vulkan (or OpenGL 4.6)" PC_NEEDS = "a graphics card with Vulkan (or OpenGL 4.6)"