# HG changeset patch # User Radek Brich # Date 1196357416 -3600 # Node ID f22952603f2959a47311e59818108c57a6f21f59 # Parent 4e0955fca79775b61515dad63405c55a969c8561 new C++ demo: realtime.cc (real-time scene viewer using SDL) Quaternion, Camera::rotate and Camera::move replace all printf's with infomsg wrapper don't allocate memory in Raytracer::render, just blindly write to provided address don't creat Container object in Raytracer, let user do it diff -r 4e0955fca797 -r f22952603f29 .bzrignore --- a/.bzrignore Mon Nov 26 23:12:40 2007 +0100 +++ b/.bzrignore Thu Nov 29 18:30:16 2007 +0100 @@ -3,3 +3,4 @@ demos/*.ply ccdemos/*.png ccdemos/spheres_shadow +ccdemos/realtime diff -r 4e0955fca797 -r f22952603f29 TODO --- a/TODO Mon Nov 26 23:12:40 2007 +0100 +++ b/TODO Thu Nov 29 18:30:16 2007 +0100 @@ -4,6 +4,7 @@ - save/load * update Python binding, Camera, new classes * more complex demos + * check/update triangle routines New Classes? @@ -31,7 +32,7 @@ Scene scene -- container with shapes, a camera and lights scene = new Scene() scene.setTop(top) -- top object in hierarchy -scene.setCamera(new Camera(pos, dir, angle)) +scene.setCamera(new Camera(eye, u, v, p)) scene.addLight(new PointLight(pos, color)) rt.setScene(scene) rt.render(w,h) diff -r 4e0955fca797 -r f22952603f29 ccdemos/Makefile --- a/ccdemos/Makefile Mon Nov 26 23:12:40 2007 +0100 +++ b/ccdemos/Makefile Thu Nov 29 18:30:16 2007 +0100 @@ -1,8 +1,11 @@ -CCFLAGS=-g -O0 -I../src -LDFLAGS=-L.. -lpng `python-config --libs` -objs=image.o ../*.o +CCFLAGS=-g -O3 -I../src +LDFLAGS=-L.. -pthread +RGBLIB_LDFLAGS=$(LDFLAGS) -lpng +SDL_CCFLAGS=$(CCFLAGS) $(shell sdl-config --cflags) +SDL_LDFLAGS=$(LDFLAGS) $(shell sdl-config --libs) +PYRIT_OBJS=$(shell ls ../*.o | grep -v raytracermodule) -all: image.o spheres_shadow +all: spheres_shadow realtime %.o: %.c $(CXX) -c -o $@ $*.c @@ -12,11 +15,14 @@ %: %.o (cd .. && make) - $(CXX) -o $@ $(objs) $*.o $(LDFLAGS) + $(CXX) -o $@ $(PYRIT_OBJS) $^ $(RGBLIB_LDFLAGS) image.o: image.c spheres_shadow.o: spheres_shadow.cc -spheres_shadow: spheres_shadow.o +spheres_shadow: spheres_shadow.o image.o + +realtime: realtime.cc + $(CXX) -o $@ $@.cc $(SDL_CCFLAGS) -L.. $(PYRIT_OBJS) $(SDL_LDFLAGS) clean: - rm -f spheres_shadow *.o + rm -f spheres_shadow realtime *.o diff -r 4e0955fca797 -r f22952603f29 ccdemos/realtime.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ccdemos/realtime.cc Thu Nov 29 18:30:16 2007 +0100 @@ -0,0 +1,160 @@ +#include + +#include "raytracer.h" + +int w = 512; +int h = 200; +float *render_buffer; + +Raytracer rt; +Camera cam; + +void update(SDL_Surface *screen) +{ + rt.render(w, h, render_buffer); + + if ( SDL_MUSTLOCK(screen) ) { + if ( SDL_LockSurface(screen) < 0 ) { + return; + } + } + + Uint32 *bufp = (Uint32 *)screen->pixels; + unsigned char c[3]; + for (float *fd = render_buffer; fd != render_buffer + w*h*3; fd += 3) + { + for (int i = 0; i < 3; i++) + { + if (fd[i] > 1.0) + c[i] = 255; + else + c[i] = (unsigned char)(fd[i] * 255.0); + } + *bufp = SDL_MapRGB(screen->format, c[0], c[1], c[2]); + bufp++; + } + + if ( SDL_MUSTLOCK(screen) ) { + SDL_UnlockSurface(screen); + } + + SDL_UpdateRect(screen, 0, 0, w, h); + SDL_Flip(screen); +} + +int main() +{ + /* initialize SDL */ + SDL_Surface *screen; + + if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + atexit(SDL_Quit); + + screen = SDL_SetVideoMode(w, h, 32, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_RESIZABLE); + if ( screen == NULL ) { + fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError()); + exit(1); + } + + /* initialize raytracer and prepare scene */ + render_buffer = (float *) malloc(w*h*3*sizeof(float)); + + rt.setThreads(2); + + KdTree top; + rt.setTop(&top); + + Light light1(Vector3(2.0, -5.0, -5.0), Colour(0.7, 0.3, 0.6)); + rt.addlight(&light1); + + Light light2(Vector3(-2.0, 10.0, 2.0), Colour(0.4, 0.6, 0.3)); + rt.addlight(&light2); + + Material mat_sph(Colour(1.0, 1.0, 1.0)); + for (int y=0; y<20; y++) + for (int x=0; x<20; x++) + rt.addshape(new Sphere(Vector3(x-10, (float)random()/RAND_MAX*5.0, y-10), 0.45, &mat_sph)); + + rt.setCamera(&cam); + cam.setEye(Vector3(0,0,10)); + + /* build kd-tree */ + top.optimize(); + + /* loop... */ + SDL_Event event; + bool quit = false; + float roty = 0.0, rotx = 0.0, move = 0.0; + while (!quit) + { + while (SDL_PollEvent(&event)) + { + switch (event.type) { + case SDL_VIDEORESIZE: + w = event.resize.w; + h = event.resize.h; + render_buffer = (float *) realloc(render_buffer, w*h*3*sizeof(float)); + screen = SDL_SetVideoMode(w, h, 32, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_RESIZABLE); + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) { + quit = true; + break; + } + if (event.key.keysym.sym == SDLK_LEFT) { + roty = -0.01; + break; + } + if (event.key.keysym.sym == SDLK_RIGHT) { + roty = +0.01; + break; + } + if (event.key.keysym.sym == SDLK_DOWN) { + rotx = +0.01; + break; + } + if (event.key.keysym.sym == SDLK_UP) { + rotx = -0.01; + break; + } + if (event.key.keysym.sym == SDLK_w) { + move = +0.5; + break; + } + if (event.key.keysym.sym == SDLK_s) { + move = -0.5; + break; + } + break; + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_LEFT || event.key.keysym.sym == SDLK_RIGHT) { + roty = 0.0; + break; + } + if (event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN) { + rotx = 0.0; + break; + } + if (event.key.keysym.sym == SDLK_w || event.key.keysym.sym == SDLK_s) { + move = 0.0; + break; + } + break; + case SDL_QUIT: + quit = true; + } + } + cam.rotate(Quaternion(cos(roty),0,sin(roty),0).normalize()); + cam.rotate(Quaternion(cos(rotx),cam.u[0]*sin(rotx),0,cam.u[2]*sin(rotx)).normalize()); + cam.u.y = 0; + cam.u.normalize(); + cam.move(move,0,0); + update(screen); + } + + free(render_buffer); +} diff -r 4e0955fca797 -r f22952603f29 ccdemos/spheres_shadow.cc --- a/ccdemos/spheres_shadow.cc Mon Nov 26 23:12:40 2007 +0100 +++ b/ccdemos/spheres_shadow.cc Thu Nov 29 18:30:16 2007 +0100 @@ -35,7 +35,8 @@ int w = 800; int h = 600; - float *fdata = rt.render(w, h); + float *fdata = (float *) malloc(w*h*3*sizeof(float)); + rt.render(w, h, fdata); struct image *img; new_image(&img, w, h, 3); diff -r 4e0955fca797 -r f22952603f29 src/common.h --- a/src/common.h Mon Nov 26 23:12:40 2007 +0100 +++ b/src/common.h Thu Nov 29 18:30:16 2007 +0100 @@ -1,9 +1,21 @@ #ifndef COMMON_H #define COMMON_H +#include +#include #include #define Eps 1e-6 #define Inf FLT_MAX +inline void infomsg(const char *format, ...) +{ +#ifndef PYRIT_QUITE + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); #endif +} + +#endif diff -r 4e0955fca797 -r f22952603f29 src/kdtree.cc --- a/src/kdtree.cc Mon Nov 26 23:12:40 2007 +0100 +++ b/src/kdtree.cc Thu Nov 29 18:30:16 2007 +0100 @@ -1,6 +1,7 @@ #include #include +#include "common.h" #include "kdtree.h" class SortableShape @@ -300,6 +301,7 @@ void KdTree::build() { + infomsg("* building kd-tree\n"); root = new KdNode(); ShapeList::iterator shape; for (shape = shapes.begin(); shape != shapes.end(); shape++) diff -r 4e0955fca797 -r f22952603f29 src/raytracer.cc --- a/src/raytracer.cc Mon Nov 26 23:12:40 2007 +0100 +++ b/src/raytracer.cc Thu Nov 29 18:30:16 2007 +0100 @@ -11,7 +11,6 @@ #include #include -#include "common.h" #include "raytracer.h" // Hammersley spherical point distribution @@ -195,22 +194,13 @@ return (void *)d; } -float *Raytracer::render(int w, int h) +void Raytracer::render(int w, int h, float *buffer) { - if (!camera || !top) - return NULL; - - float *data; - - data = (float *) malloc(w*h*3*sizeof(float)); - if (!data) - return NULL; + if (!camera || !top || !buffer) + return; RenderrowData *d; - printf("* building kd-tree\n"); - top->optimize(); - float S = 0.5/w; Vector3 dfix = camera->u*(-w/2.0*S/camera->f) + camera->v*(h/2.0*S/camera->f) + camera->p; @@ -218,7 +208,7 @@ Vector3 dy = camera->v * (-S/camera->f); #ifdef PTHREADS - printf("* pthreads enabled, using %d threads\n", num_threads); + infomsg("* pthreads enabled, using %d threads\n", num_threads); pthread_t threads[num_threads]; for (int t = 0; t < num_threads; t++) threads[t] = pthread_self(); @@ -226,8 +216,9 @@ #endif /* for each pixel... */ - printf("* rendering row 0 ( 0%% done)"); - for (int y = 0; y < h; y++) { + infomsg("* rendering row 0 ( 0%% done)"); + for (int y = 0; y < h; y++) + { d = (RenderrowData*) malloc(sizeof(RenderrowData)); d->rt = this; d->w = w; @@ -237,12 +228,12 @@ #if OVERSAMPLING d->dy = dy; #endif - d->iter = data + y*3*w; + d->iter = buffer + y*3*w; #ifdef PTHREADS /* create new thread and increase 't' */ int rc = pthread_create(&threads[t++], NULL, renderrow, (void *)d); if (rc) { - printf("\nERROR: return code from pthread_create() is %d\n", rc); + infomsg("\nERROR: return code from pthread_create() is %d\n", rc); exit(1); } /* when 't' owerflows, reset it */ @@ -258,18 +249,16 @@ free(d); #endif dfix += dy; - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4d (%2d%% done)", y, y*100/(h-1)); + infomsg("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4d (%2d%% done)", y, y*100/(h-1)); } - printf("\n"); + infomsg("\n"); #ifdef PTHREADS - printf("* waiting for threads to finish\n"); + infomsg("* waiting for threads to finish\n"); for (t = 0; t < num_threads; t++) if (pthread_join(threads[t], (void**)&d) == 0) free(d); #endif - - return data; } void Raytracer::addlight(Light *light) diff -r 4e0955fca797 -r f22952603f29 src/raytracer.h --- a/src/raytracer.h Mon Nov 26 23:12:40 2007 +0100 +++ b/src/raytracer.h Thu Nov 29 18:30:16 2007 +0100 @@ -10,6 +10,7 @@ #include +#include "common.h" #include "kdtree.h" #include "scene.h" @@ -38,14 +39,15 @@ Vector3 SphereDistribute(int i, int n, float extent, Vector3 &normal); public: - Raytracer(): camera(NULL), lights(), bg_colour(0.0, 0.0, 0.0), - ao_samples(0), num_threads(4) { top = new KdTree(); }; - ~Raytracer() { delete top; }; - float *render(int w, int h); + Raytracer(): top(NULL), camera(NULL), lights(), bg_colour(0.0, 0.0, 0.0), + ao_samples(0), num_threads(4) {}; + void render(int w, int h, float *buffer); Colour raytrace(Ray &ray, int depth, Shape *origin_shape); void addshape(Shape *shape) { top->addShape(shape); }; void addlight(Light *light); void setCamera(Camera *cam) { camera = cam; }; + void setTop(Container *atop) { top = atop; }; + Container *getTop() { return top; }; void ambientocclusion(int samples, float distance, float angle); void setThreads(int num) { num_threads = num; }; diff -r 4e0955fca797 -r f22952603f29 src/raytracermodule.cc --- a/src/raytracermodule.cc Mon Nov 26 23:12:40 2007 +0100 +++ b/src/raytracermodule.cc Thu Nov 29 18:30:16 2007 +0100 @@ -423,6 +423,7 @@ v->raytracer = new Raytracer(); v->children = new vector(); v->raytracer->setCamera(new Camera()); + v->raytracer->setTop(new KdTree()); return (PyObject*)v; } @@ -453,7 +454,9 @@ return NULL; printf("[pyrit] Raytracing...\n"); - data = ((RaytracerObject *)self)->raytracer->render(w, h); + ((RaytracerObject *)self)->raytracer->getTop()->optimize(); + data = (float *) malloc(w*h*3*sizeof(float)); + ((RaytracerObject *)self)->raytracer->render(w, h, data); if (!data) { Py_INCREF(Py_None); return Py_None; diff -r 4e0955fca797 -r f22952603f29 src/scene.cc --- a/src/scene.cc Mon Nov 26 23:12:40 2007 +0100 +++ b/src/scene.cc Thu Nov 29 18:30:16 2007 +0100 @@ -10,6 +10,48 @@ #include "common.h" #include "scene.h" +void Camera::rotate(const Quaternion &q) +{ + Quaternion res; + res = q * Quaternion(u) * conjugate(q); + u = res.toVector(); + res = q * Quaternion(v) * conjugate(q); + v = res.toVector(); + res = q * Quaternion(p) * conjugate(q); + p = res.toVector(); + p.normalize(); + u.normalize(); + v.normalize(); +/* // optimized version + float t2 = q.a*q.b; + float t3 = q.a*q.c; + float t4 = q.a*q.d; + float t5 = -q.b*q.b; + float t6 = q.b*q.c; + float t7 = q.b*q.d; + float t8 = -q.c*q.c; + float t9 = q.c*q.d; + float t10 = -q.d*q.d; + float x,y,z; + x = 2*( (t8 + t10)*p.x + (t6 - t4)*p.y + (t3 + t7)*p.z ) + p.x; + y = 2*( (t4 + t6)*p.x + (t5 + t10)*p.y + (t9 - t2)*p.z ) + p.y; + z = 2*( (t7 - t3)*p.x + (t2 + t9)*p.y + (t5 + t8)*p.z ) + p.z; + p = Vector3(x,y,z); + x = 2*( (t8 + t10)*u.x + (t6 - t4)*u.y + (t3 + t7)*u.z ) + u.x; + y = 2*( (t4 + t6)*u.x + (t5 + t10)*u.y + (t9 - t2)*u.z ) + u.y; + z = 2*( (t7 - t3)*u.x + (t2 + t9)*u.y + (t5 + t8)*u.z ) + u.z; + u = Vector3(x,y,z); + x = 2*( (t8 + t10)*v.x + (t6 - t4)*v.y + (t3 + t7)*v.z ) + v.x; + y = 2*( (t4 + t6)*v.x + (t5 + t10)*v.y + (t9 - t2)*v.z ) + v.y; + z = 2*( (t7 - t3)*v.x + (t2 + t9)*v.y + (t5 + t8)*v.z ) + v.z; + v = Vector3(x,y,z);*/ +} + +void Camera::move(const float fw, const float left, const float up) +{ + eye = eye + fw*p + left*u + up*v; +} + /* http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm */ bool BBox::intersect(const Ray &ray, float &a, float &b) { diff -r 4e0955fca797 -r f22952603f29 src/scene.h --- a/src/scene.h Mon Nov 26 23:12:40 2007 +0100 +++ b/src/scene.h Thu Nov 29 18:30:16 2007 +0100 @@ -24,6 +24,40 @@ o(ao), dir(adir) {}; }; +class Quaternion +{ +public: + float a,b,c,d; + Quaternion(): a(0), b(0), c(0), d(0) {}; + Quaternion(const float aa, const float ab, const float ac, const float ad): + a(aa), b(ab), c(ac), d(ad) {}; + Quaternion(const Vector3& v): a(1), b(v.x), c(v.y), d(v.z) {}; + + Vector3 toVector() { return Vector3(b/a, c/a, d/a); }; + + Quaternion normalize() + { + float f = 1.0f / sqrtf(a * a + b * b + c * c + d * d); + a *= f; + b *= f; + c *= f; + d *= f; + return *this; + }; + friend Quaternion operator*(const Quaternion &q1, const Quaternion &q2) + { + return Quaternion( + q1.a*q2.a - q1.b*q2.b - q1.c*q2.c - q1.d*q2.d, + q1.a*q2.b + q1.b*q2.a + q1.c*q2.d - q1.d*q2.c, + q1.a*q2.c + q1.c*q2.a + q1.d*q2.b - q1.b*q2.d, + q1.a*q2.d + q1.d*q2.a + q1.b*q2.c - q1.c*q2.b); + }; + friend Quaternion conjugate(const Quaternion &q) + { + return Quaternion(q.a, -q.b, -q.c, -q.d); + } +}; + class Camera { public: @@ -34,7 +68,9 @@ Camera(const Vector3 &C, const Vector3 &ap, const Vector3 &au, const Vector3 &av): eye(C), p(ap), u(au), v(av), f(3.14/4.0) {}; void setEye(const Vector3 &aeye) { eye = aeye; }; - void setFocusLength(const float af) { f = af; }; + void setFocalLength(const float af) { f = af; }; + void rotate(const Quaternion &q); + void move(const float fw, const float left, const float up); }; /* axis-aligned bounding box */ @@ -90,8 +126,8 @@ texture.colour = acolour; ambient = 0.1; diffuse = 0.5; - specular = 1.0; - shininess = 20.0; + specular = 0.1; + shininess = 0.5; reflection = 0.5; } };