feat(examples): add the C++ benchmark and three test targets
A C++ consumer of the C archives, plus maths, scene and image tests driven by meowc test.
This commit is contained in:
4 files changed
+267
No files matched your search
@@ -0,0 +1,91 @@
|
||||
#include "rt_config.h"
|
||||
#include "rt_image.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check(const char *what, int cond) {
|
||||
if (!cond) {
|
||||
fprintf(stderr, " %s: failed\n", what);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static long read_all(const char *path, unsigned char **out) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
unsigned char *buf = malloc((size_t)n);
|
||||
if (!buf || fread(buf, 1u, (size_t)n, f) != (size_t)n) {
|
||||
free(buf);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
*out = buf;
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned long be32(const unsigned char *p) {
|
||||
return ((unsigned long)p[0] << 24) | ((unsigned long)p[1] << 16) | ((unsigned long)p[2] << 8) | p[3];
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
image *im = image_new(17, 11);
|
||||
check("the image allocates", im != NULL);
|
||||
if (!im) return 1;
|
||||
|
||||
check("width is kept", im->width == 17);
|
||||
check("the buffer starts black", im->rgb[0] == 0 && im->rgb[17 * 11 * 3 - 1] == 0);
|
||||
|
||||
for (int y = 0; y < im->height; y++)
|
||||
for (int x = 0; x < im->width; x++) {
|
||||
unsigned char *px = im->rgb + ((size_t)y * 17 + (size_t)x) * 3;
|
||||
px[0] = (unsigned char)(x * 15);
|
||||
px[1] = (unsigned char)(y * 23);
|
||||
px[2] = 128;
|
||||
}
|
||||
|
||||
check("a zero sized image is refused", image_new(0, 4) == NULL);
|
||||
|
||||
const char *ppm = "test-out.ppm";
|
||||
check("ppm writes", image_write_ppm(im, ppm) == 0);
|
||||
unsigned char *buf = NULL;
|
||||
long n = read_all(ppm, &buf);
|
||||
check("ppm has a header and every pixel", n == (long)(strlen("P6\n17 11\n255\n") + 17 * 11 * 3));
|
||||
check("ppm starts with the magic", n > 2 && buf[0] == 'P' && buf[1] == '6');
|
||||
free(buf);
|
||||
remove(ppm);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
const char *png = "test-out.png";
|
||||
check("png writes", image_write_png(im, png) == 0);
|
||||
n = read_all(png, &buf);
|
||||
check("png is not empty", n > 8);
|
||||
if (n > 8) {
|
||||
static const unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
|
||||
check("png signature", memcmp(buf, sig, 8) == 0);
|
||||
check("first chunk is IHDR", memcmp(buf + 12, "IHDR", 4) == 0);
|
||||
check("IHDR carries the width", be32(buf + 16) == 17);
|
||||
check("IHDR carries the height", be32(buf + 20) == 11);
|
||||
check("eight bits per channel", buf[24] == 8);
|
||||
check("truecolour", buf[25] == 2);
|
||||
check("last chunk is IEND", memcmp(buf + n - 8, "IEND", 4) == 0);
|
||||
}
|
||||
free(buf);
|
||||
remove(png);
|
||||
check("the default extension follows zlib", strcmp(image_default_extension(), "png") == 0);
|
||||
#else
|
||||
check("without zlib the default is ppm", strcmp(image_default_extension(), "ppm") == 0);
|
||||
#endif
|
||||
|
||||
image_free(im);
|
||||
image_free(NULL); /* must tolerate a null pointer */
|
||||
|
||||
if (failures == 0) printf(" image: every assertion held\n");
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "rt.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void near(const char *what, double got, double want, double tol) {
|
||||
if (fabs(got - want) > tol) {
|
||||
fprintf(stderr, " %s: got %.9f, want %.9f\n", what, got, want);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void check(const char *what, int cond) {
|
||||
if (!cond) {
|
||||
fprintf(stderr, " %s: failed\n", what);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
vec3 a = v3(1.0, 2.0, 3.0), b = v3(-4.0, 5.0, -6.0);
|
||||
|
||||
near("dot", v3dot(a, b), -4.0 + 10.0 - 18.0, 1e-12);
|
||||
near("len", v3len(v3(3.0, 4.0, 0.0)), 5.0, 1e-12);
|
||||
near("norm is unit", v3len(v3norm(b)), 1.0, 1e-12);
|
||||
|
||||
vec3 c = v3cross(a, b);
|
||||
near("cross is perpendicular to a", v3dot(c, a), 0.0, 1e-12);
|
||||
near("cross is perpendicular to b", v3dot(c, b), 0.0, 1e-12);
|
||||
|
||||
/* A ray striking a surface head on comes straight back. */
|
||||
vec3 r = v3reflect(v3(0.0, -1.0, 0.0), v3(0.0, 1.0, 0.0));
|
||||
near("reflect x", r.x, 0.0, 1e-12);
|
||||
near("reflect y", r.y, 1.0, 1e-12);
|
||||
|
||||
vec3 mid = v3lerp(v3(0.0, 0.0, 0.0), v3(2.0, 4.0, 6.0), 0.5);
|
||||
near("lerp midpoint", mid.y, 2.0, 1e-12);
|
||||
|
||||
near("zero length normalises to itself", v3len(v3norm(v3(0, 0, 0))), 0.0, 1e-12);
|
||||
|
||||
/* The generator must be reproducible from a seed, and stay in range. */
|
||||
rng x, y;
|
||||
rng_seed(&x, 12345);
|
||||
rng_seed(&y, 12345);
|
||||
double sum = 0.0, lo = 1.0, hi = 0.0;
|
||||
for (int i = 0; i < 200000; i++) {
|
||||
double u = rng_unit(&x);
|
||||
check("same seed gives the same stream", u == rng_unit(&y));
|
||||
if (u < lo) lo = u;
|
||||
if (u > hi) hi = u;
|
||||
sum += u;
|
||||
}
|
||||
check("stays inside [0,1)", lo >= 0.0 && hi < 1.0);
|
||||
near("mean is near one half", sum / 200000.0, 0.5, 0.005);
|
||||
|
||||
for (int i = 0; i < 5000; i++) check("unit vector is unit", fabs(v3len(rng_unit_vector(&x)) - 1.0) < 1e-9);
|
||||
for (int i = 0; i < 5000; i++) check("sample sits inside the sphere", v3len2(rng_in_sphere(&x)) < 1.0);
|
||||
|
||||
if (failures == 0) printf(" math: every assertion held\n");
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "rt_scene.h"
|
||||
#include "studio_scene.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check(const char *what, int cond) {
|
||||
if (!cond) {
|
||||
fprintf(stderr, " %s: failed\n", what);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
check("the generated scene has spheres", studio_sphere_count > 0);
|
||||
|
||||
/* One unit sphere at the origin, hit straight down the -z axis. */
|
||||
static const sphere one[] = {{{0, 0, 0}, 1.0, {MAT_LAMBERT, {0.5, 0.5, 0.5}, 0.0}}};
|
||||
scene sc = {one, 1, {0, 0, 0}};
|
||||
|
||||
ray r = {{0, 0, 5}, {0, 0, -1}};
|
||||
hit h;
|
||||
check("a ray aimed at the sphere hits it", scene_hit(&sc, r, 1e-4, 1e30, &h));
|
||||
check("it hits the near face", fabs(h.t - 4.0) < 1e-9);
|
||||
check("the normal faces the ray", fabs(h.normal.z - 1.0) < 1e-9);
|
||||
check("the hit is on the outside", h.front == 1);
|
||||
|
||||
ray miss = {{0, 5, 5}, {0, 0, -1}};
|
||||
check("a ray that passes above misses", !scene_hit(&sc, miss, 1e-4, 1e30, &h));
|
||||
|
||||
/* Starting inside, the first crossing is the far wall. */
|
||||
ray inside = {{0, 0, 0}, {0, 0, -1}};
|
||||
check("a ray from the centre still hits", scene_hit(&sc, inside, 1e-4, 1e30, &h));
|
||||
check("and reports the inside face", h.front == 0);
|
||||
|
||||
/* The interval bounds are respected. */
|
||||
check("a hit beyond tmax is rejected", !scene_hit(&sc, r, 1e-4, 1.0, &h));
|
||||
|
||||
/* Nearest sphere wins when two overlap the ray. */
|
||||
static const sphere two[] = {
|
||||
{{0, 0, 0}, 1.0, {MAT_LAMBERT, {1, 0, 0}, 0.0}},
|
||||
{{0, 0, 2.5}, 0.5, {MAT_METAL, {0, 1, 0}, 0.0}},
|
||||
};
|
||||
scene both = {two, 2, {0, 0, 0}};
|
||||
check("the closer sphere is chosen", scene_hit(&both, r, 1e-4, 1e30, &h) && h.mat.kind == MAT_METAL);
|
||||
|
||||
if (failures == 0) printf(" scene: every assertion held\n");
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in new issue
Block a user