new C++ demo: realtime.cc (real-time scene viewer using SDL) pyrit
authorRadek Brich <radek.brich@devl.cz>
Thu, 29 Nov 2007 18:30:16 +0100
branchpyrit
changeset 20 f22952603f29
parent 19 4e0955fca797
child 21 79b516a3803d
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
.bzrignore
TODO
ccdemos/Makefile
ccdemos/realtime.cc
ccdemos/spheres_shadow.cc
src/common.h
src/kdtree.cc
src/raytracer.cc
src/raytracer.h
src/raytracermodule.cc
src/scene.cc
src/scene.h
--- 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;
 	}
 };