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
--- 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
--- 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)
--- 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
--- /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 <SDL.h>
+
+#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);
+}
--- 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);
--- 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 <stdio.h>
+#include <stdarg.h>
#include <float.h>
#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
--- 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 <algorithm>
#include <stack>
+#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++)
--- 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 <stdio.h>
#include <malloc.h>
-#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)
--- 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 <vector>
+#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; };
--- 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<PyObject*>();
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;
--- 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)
{
--- 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;
}
};