smooth triangles (aka Phong shading)
extend Python binding to support vertex normals and smooth triangles
make bunny.py and realtime_dragon smooth, and fix other demos for new triangle constructor
add Vector::operator/=
#include <SDL.h>
#include "raytracer.h"
#include <iostream>
#include <fstream>
int w = 320;
int h = 200;
Float *render_buffer;
Raytracer rt;
Camera cam;
void load_ply(const char *filename, Material *mat, Float scale)
{
vector<NormalVertex*> vertices;
vector<Vector3> normals;
vector<int> vertex_face_num;
ifstream f(filename);
string token = "a";
if (!f.is_open())
{
cout << "File not found: " << filename <<endl;
exit(1);
}
// read header
int vertex_num, face_num;
while (token != "end_header")
{
f >> token;
if (token == "element")
{
f >> token;
if (token == "vertex")
f >> vertex_num;
if (token == "face")
f >> face_num;
}
}
// read vertices
Vector3 P;
int num = vertex_num;
while (num--)
{
f >> P.x >> P.y >> P.z;
P.x = -scale*P.x;
P.y = scale*P.y - 3.6;
P.z = -scale*P.z;
vertices.push_back(new NormalVertex(P));
normals.push_back(Vector3());
vertex_face_num.push_back(0);
}
// read faces
Triangle *face;
int v1, v2, v3;
while (face_num--)
{
f >> num;
if (num != 3)
{
printf("ply error: faces of %d vertices not supported", num);
return;
}
f >> v1 >> v2 >> v3;
face = new Triangle(vertices.at(v1), vertices.at(v2), vertices.at(v3), mat);
rt.addshape(face);
face->setSmooth();
normals.at(v1) += face->getNormal();
vertex_face_num.at(v1)++;
normals.at(v2) += face->getNormal();
vertex_face_num.at(v2)++;
normals.at(v3) += face->getNormal();
vertex_face_num.at(v3)++;
}
for (int i; i < vertex_num; i++)
{
normals.at(i) /= vertex_face_num.at(i);
normals.at(i).normalize();
vertices.at(i)->N = normals.at(i);
}
f.close();
}
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);
if (screen->flags & SDL_DOUBLEBUF)
SDL_Flip(screen);
else
SDL_UpdateRect(screen, 0, 0, w, h);
}
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_SWSURFACE|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(1);
rt.setMaxDepth(3);
KdTree top;
rt.setTop(&top);
Light light1(Vector3(-5.0, 2.0, 8.0), Colour(0.9, 0.3, 0.6));
light1.castShadows(false);
rt.addlight(&light1);
//Light light2(Vector3(-2.0, 10.0, 2.0), Colour(0.4, 0.6, 0.3));
//light2.castShadows(false);
//rt.addlight(&light2);
Material mat(Colour(0.9, 0.9, 0.9));
load_ply("../models/dragon/dragon_vrip_res4.ply", &mat, 29);
rt.setCamera(&cam);
cam.setEye(Vector3(0,0,10));
/* build kd-tree */
top.setMaxDepth(100);
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);
}