From 5eac8b795c5a44469b749ecb4cf7a68038055b5b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 6 Dec 2011 00:56:37 -0500 Subject: Add physics test. --- libdimension/gl.c | 18 +-- libdimension/png.c | 18 +-- libdimension/rgba16.c | 10 +- libdimension/tests/Makefile.am | 6 +- libdimension/tests/physics.c | 247 +++++++++++++++++++++++++++++++++++++++++ libdimension/tests/physics.sh | 4 + 6 files changed, 279 insertions(+), 24 deletions(-) create mode 100644 libdimension/tests/physics.c create mode 100755 libdimension/tests/physics.sh diff --git a/libdimension/gl.c b/libdimension/gl.c index 1250307..396205b 100644 --- a/libdimension/gl.c +++ b/libdimension/gl.c @@ -40,8 +40,8 @@ dmnsn_gl_optimize_canvas(dmnsn_canvas *canvas) int dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) { - GLushort *pixels; /* Array of 16-bit ints in RGBA order */ - GLushort *pixel; + GLubyte *pixels; /* Array of 8-bit ints in RGBA order */ + GLubyte *pixel; dmnsn_color color; size_t width = canvas->width; @@ -50,13 +50,13 @@ dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) /* Check if we can optimize this */ DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer *, i, canvas->optimizers) { if (i->optimizer_fn == dmnsn_rgba16_optimizer_fn) { - glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_SHORT, i->ptr); + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, i->ptr); return glGetError() == GL_NO_ERROR ? 0 : 1; } } /* We couldn't, so transform the canvas to RGB now */ - pixels = dmnsn_malloc(4*width*height*sizeof(GLushort)); + pixels = dmnsn_malloc(4*width*height*sizeof(GLubyte)); for (size_t y = 0; y < height; ++y) { for (size_t x = 0; x < width; ++x) { @@ -67,14 +67,14 @@ dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) color = dmnsn_color_to_sRGB(color); color = dmnsn_color_saturate(color); - pixel[0] = lround(color.R*UINT16_MAX); - pixel[1] = lround(color.G*UINT16_MAX); - pixel[2] = lround(color.B*UINT16_MAX); - pixel[3] = lround(color.trans*UINT16_MAX); + pixel[0] = lround(color.R*UINT8_MAX); + pixel[1] = lround(color.G*UINT8_MAX); + pixel[2] = lround(color.B*UINT8_MAX); + pixel[3] = lround(color.trans*UINT8_MAX); } } - glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_SHORT, pixels); + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); dmnsn_free(pixels); return glGetError() == GL_NO_ERROR ? 0 : 1; diff --git a/libdimension/png.c b/libdimension/png.c index 7631804..44bf978 100644 --- a/libdimension/png.c +++ b/libdimension/png.c @@ -145,7 +145,7 @@ dmnsn_png_write_canvas_thread(void *ptr) } /* libpng will longjmp here if it encounters an error from here on */ - uint16_t *row = NULL; + uint8_t *row = NULL; if (setjmp(png_jmpbuf(png_ptr))) { /* libpng error */ dmnsn_free(row); @@ -157,8 +157,8 @@ dmnsn_png_write_canvas_thread(void *ptr) /* Associate file with the libpng write struct */ png_init_io(png_ptr, payload->file); - /* Set header correctly for 16-bit sRGB image */ - png_set_IHDR(png_ptr, info_ptr, width, height, 16, + /* Set header correctly for 8-bit sRGB image */ + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); @@ -181,7 +181,7 @@ dmnsn_png_write_canvas_thread(void *ptr) if (i->optimizer_fn == dmnsn_rgba16_optimizer_fn) { for (size_t y = 0; y < height; ++y) { /* Invert the rows. PNG coordinates are fourth quadrant. */ - uint16_t *row = (uint16_t *)i->ptr + 4*(height - y - 1)*width; + uint8_t *row = (uint8_t *)i->ptr + 4*(height - y - 1)*width; png_write_row(png_ptr, (png_bytep)row); dmnsn_future_increment(payload->future); } @@ -195,7 +195,7 @@ dmnsn_png_write_canvas_thread(void *ptr) } /* Allocate the temporary row of RGBA values */ - row = dmnsn_malloc(4*sizeof(uint16_t)*width); + row = dmnsn_malloc(4*sizeof(uint8_t)*width); /* Write the pixels */ for (size_t y = 0; y < height; ++y) { @@ -207,10 +207,10 @@ dmnsn_png_write_canvas_thread(void *ptr) color = dmnsn_color_to_sRGB(color); color = dmnsn_color_saturate(color); - row[4*x] = lround(color.R*UINT16_MAX); - row[4*x + 1] = lround(color.G*UINT16_MAX); - row[4*x + 2] = lround(color.B*UINT16_MAX); - row[4*x + 3] = lround(color.trans*UINT16_MAX); + row[4*x] = lround(color.R*UINT8_MAX); + row[4*x + 1] = lround(color.G*UINT8_MAX); + row[4*x + 2] = lround(color.B*UINT8_MAX); + row[4*x + 3] = lround(color.trans*UINT8_MAX); } /* Write the row */ diff --git a/libdimension/rgba16.c b/libdimension/rgba16.c index 980a2d3..5dee049 100644 --- a/libdimension/rgba16.c +++ b/libdimension/rgba16.c @@ -49,15 +49,15 @@ void dmnsn_rgba16_optimizer_fn(const dmnsn_canvas *canvas, dmnsn_canvas_optimizer optimizer, size_t x, size_t y) { - uint16_t *pixel = (uint16_t *)optimizer.ptr + 4*(y*canvas->width + x); + uint8_t *pixel = (uint8_t *)optimizer.ptr + 4*(y*canvas->width + x); dmnsn_color color; color = dmnsn_canvas_get_pixel(canvas, x, y); color = dmnsn_remove_filter(color); color = dmnsn_color_to_sRGB(color); color = dmnsn_color_saturate(color); - pixel[0] = lround(color.R*UINT16_MAX); - pixel[1] = lround(color.G*UINT16_MAX); - pixel[2] = lround(color.B*UINT16_MAX); - pixel[3] = lround(color.trans*UINT16_MAX); + pixel[0] = lround(color.R*UINT8_MAX); + pixel[1] = lround(color.G*UINT8_MAX); + pixel[2] = lround(color.B*UINT8_MAX); + pixel[3] = lround(color.trans*UINT8_MAX); } diff --git a/libdimension/tests/Makefile.am b/libdimension/tests/Makefile.am index f44cdd6..f4ec515 100644 --- a/libdimension/tests/Makefile.am +++ b/libdimension/tests/Makefile.am @@ -28,7 +28,8 @@ check_PROGRAMS = warning.test \ png.test \ gl.test \ render.test \ - cxx.test + cxx.test \ + physics.test TESTS = $(check_PROGRAMS) XFAIL_TESTS = warning-as-error.test error.test @@ -80,5 +81,8 @@ render_test_LDADD = libdimension-tests.la cxx_test_SOURCES = cxx.cpp cxx_test_LDADD = libdimension-tests.la +physics_test_SOURCES = physics.c +physics_test_LDADD = libdimension-tests.la + clean-local: rm -f *.png diff --git a/libdimension/tests/physics.c b/libdimension/tests/physics.c new file mode 100644 index 0000000..14eeb07 --- /dev/null +++ b/libdimension/tests/physics.c @@ -0,0 +1,247 @@ +#include "dimension.h" +#include + +typedef struct sphere { + dmnsn_vector center; + dmnsn_vector velocity; + double radius; + dmnsn_color color; +} sphere; + +sphere +make_sphere(size_t x, size_t y, size_t z, size_t size) +{ + --size; + + double dx = sin(2*M_PI*x/size); + double dy = sin(2*M_PI*y/size); + double dz = sin(2*M_PI*z/size); + dmnsn_vector c = dmnsn_vector_sub( + dmnsn_vector_add( + dmnsn_vector_mul( + 5.0/size, + dmnsn_new_vector(x, y, z) + ), + dmnsn_vector_div( + dmnsn_new_vector(dy + dz, dx + dz, dx + dy), + 4.0 + ) + ), + dmnsn_new_vector(2.5, 2.5, 2.5) + ); + + double r = 2.0/size; + + dmnsn_color color = dmnsn_color_from_sRGB( + dmnsn_new_color((double)x/size, (double)y/size, (double)z/size) + ); + + sphere s = { + .center = c, + .velocity = dmnsn_zero, + .radius = r, + .color = color, + }; + return s; +} + +dmnsn_array * +make_spheres() +{ + const size_t size = 10; + dmnsn_array *spheres = dmnsn_new_array(sizeof(sphere)); + for (size_t x = 0; x < size; ++x) { + for (size_t y = 0; y < size; ++y) { + for (size_t z = 0; z < size; ++z) { + sphere s = make_sphere(x, y, z, size); + dmnsn_array_push(spheres, &s); + } + } + } + + return spheres; +} + +dmnsn_scene * +make_scene(dmnsn_array *spheres, dmnsn_canvas *canvas, dmnsn_camera *camera) +{ + dmnsn_scene *scene = dmnsn_new_scene(); + DMNSN_INCREF(canvas); + scene->canvas = canvas; + DMNSN_INCREF(camera); + scene->camera = camera; + + scene->default_texture->pigment = dmnsn_new_solid_pigment(dmnsn_black); + scene->default_texture->finish.ambient = dmnsn_new_basic_ambient( + dmnsn_color_from_sRGB(dmnsn_color_mul(0.25, dmnsn_white)) + ); + scene->default_texture->finish.diffuse = dmnsn_new_lambertian( + dmnsn_sRGB_inverse_gamma(0.8) + ); + + scene->background = dmnsn_new_solid_pigment( + dmnsn_color_from_sRGB( + dmnsn_color_mul(0.5, dmnsn_new_color(0.73, 0.90, 0.97)) + ) + ); + + DMNSN_ARRAY_FOREACH (sphere *, s, spheres) { + dmnsn_object *sph = dmnsn_new_sphere(); + + sph->texture = dmnsn_new_texture(); + sph->texture->pigment = dmnsn_new_solid_pigment(s->color); + double maxcomponent = dmnsn_max( + dmnsn_max(s->color.R, s->color.G), + s->color.B + ); + dmnsn_color reflcolor; + if (maxcomponent >= dmnsn_epsilon) { + reflcolor = dmnsn_color_mul(0.5/maxcomponent, s->color); + } else { + reflcolor = dmnsn_color_mul(0.25, dmnsn_white); + } + sph->texture->finish.reflection = dmnsn_new_basic_reflection( + dmnsn_black, reflcolor, 1.0 + ); + + sph->trans = dmnsn_matrix_mul( + dmnsn_translation_matrix(s->center), + dmnsn_scale_matrix(dmnsn_new_vector(s->radius, s->radius, s->radius)) + ); + + dmnsn_array_push(scene->objects, &sph); + } + + dmnsn_object *plane = dmnsn_new_plane(dmnsn_y); + plane->trans = dmnsn_translation_matrix(dmnsn_vector_mul(-4.0, dmnsn_y)); + plane->texture = dmnsn_new_texture(); + plane->texture->pigment = dmnsn_new_solid_pigment( + dmnsn_color_from_sRGB(dmnsn_new_color(0.73, 0.90, 0.97)) + ); + plane->texture->finish.ambient = dmnsn_new_basic_ambient( + dmnsn_color_from_sRGB(dmnsn_color_mul(0.5, dmnsn_white)) + ); + plane->texture->finish.diffuse = dmnsn_new_lambertian( + dmnsn_sRGB_inverse_gamma(0.7) + ); + dmnsn_array_push(scene->objects, &plane); + + dmnsn_color lcolor = dmnsn_color_mul(1.0/4.0, dmnsn_white); + dmnsn_light *light; + light = dmnsn_new_point_light(dmnsn_new_vector(-3.0, 0.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(-1.0, 0.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(+1.0, 0.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(+3.0, 0.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(-3.0, 5.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(-1.0, 5.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(+1.0, 5.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + light = dmnsn_new_point_light(dmnsn_new_vector(+3.0, 5.0, -5.0), lcolor); + dmnsn_array_push(scene->lights, &light); + + scene->nthreads = 12; /* XXX */ + return scene; +} + +void +integrate_spheres(dmnsn_array *spheres, double h) +{ + static const double g = 5.0; + + /* Inter-object collision detection */ + DMNSN_ARRAY_FOREACH (sphere *, s1, spheres) { + DMNSN_ARRAY_FOREACH (sphere *, s2, spheres) { + if (s1 == s2) continue; + + dmnsn_vector deltar = dmnsn_vector_sub(s1->center, s2->center); + dmnsn_vector deltav = dmnsn_vector_sub(s1->velocity, s2->velocity); + if (dmnsn_vector_norm(deltar) <= s1->radius + s2->radius + && dmnsn_vector_dot(deltar, deltav) < 0.0) + { + dmnsn_vector x = dmnsn_vector_normalized(deltar); + dmnsn_vector v1 = s1->velocity; + double x1 = dmnsn_vector_dot(x, v1); + dmnsn_vector v1x = dmnsn_vector_mul(x1, x); + dmnsn_vector v1y = dmnsn_vector_sub(v1, v1x); + + x = dmnsn_vector_negate(x); + dmnsn_vector v2 = s2->velocity; + double x2 = dmnsn_vector_dot(x, v2); + dmnsn_vector v2x = dmnsn_vector_mul(x2, x); + dmnsn_vector v2y = dmnsn_vector_sub(v2, v2x); + + s1->velocity = dmnsn_vector_add(v2x, v1y); + s2->velocity = dmnsn_vector_add(v1x, v2y); + } + } + } + + /* Floor collision detection */ + DMNSN_ARRAY_FOREACH (sphere *, s, spheres) { + if (s->center.y - s->radius <= -4.0) { + s->velocity.y = fabs(s->velocity.y); + } + } + + /* Advance by the timestep */ + DMNSN_ARRAY_FOREACH (sphere *, s, spheres) { + s->center = dmnsn_vector_add(s->center, dmnsn_vector_mul(h, s->velocity)); + s->center.y -= g*h*h/2.0; + s->velocity.y -= g*h; + } +} + +int +main() +{ + dmnsn_die_on_warnings(true); + + const double h = 1.0/25.0; + const int nframes = 401; + const size_t width = 1920, height = 1080; + + dmnsn_array *spheres = make_spheres(); + + dmnsn_canvas *canvas = dmnsn_new_canvas(width, height); + dmnsn_png_optimize_canvas(canvas); + + dmnsn_camera *camera = dmnsn_new_perspective_camera(); + camera->trans = dmnsn_new_matrix( + 1.7151356822004125, -0.12253122769681897, -0.2328451577118997, 3.0, + 0.0, 0.8849477555881373, -0.4656903154237999, 6.0, + 0.46776427696374845, 0.4492811682216699, 0.8537655782769662, -11.0 + ); + + for (int i = 0; i < nframes; ++i) { + if (i > 0) { + printf("Frame %d:\t Integrating\n", i); + static const int precision = 100; + for (int j = 0; j < precision; ++j) + integrate_spheres(spheres, h/precision); + } + + printf("Frame %d:\t Rendering\n", i); + dmnsn_scene *scene = make_scene(spheres, canvas, camera); + dmnsn_ray_trace(scene); + + printf("Frame %d:\t Exporting\n", i); + char fname[] = "physics00000.png"; + sprintf(fname, "physics%05d.png", i); + FILE *image = fopen(fname, "wb"); + dmnsn_png_write_canvas(canvas, image); + fclose(image); + + dmnsn_delete_scene(scene); + } + + dmnsn_delete_camera(camera); + dmnsn_delete_canvas(canvas); + dmnsn_delete_array(spheres); + return 0; +} diff --git a/libdimension/tests/physics.sh b/libdimension/tests/physics.sh new file mode 100755 index 0000000..da07873 --- /dev/null +++ b/libdimension/tests/physics.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +ffmpeg -f image2 -i libdimension/tests/physics%05d.png -vcodec libx264 -preset medium -qp 0 -threads 12 physics.mkv +ffmpeg -i physics.mkv -s 1920x1080 -sameq -threads 12 physics1080.mkv -- cgit v1.2.3