www.pudn.com > raytracer4.zip > scene.cpp
// -----------------------------------------------------------
// scene.cpp
// 2004 - Jacco Bikker - jacco@bik5.com - www.bik5.com - <><
// -----------------------------------------------------------
#include "common.h"
#include "string.h"
#include "scene.h"
#include "raytracer.h"
namespace Raytracer {
// -----------------------------------------------------------
// Primitive class implementation
// -----------------------------------------------------------
void Primitive::SetName( char* a_Name )
{
delete m_Name;
m_Name = new char[strlen( a_Name ) + 1];
strcpy( m_Name, a_Name );
}
// -----------------------------------------------------------
// Material class implementation
// -----------------------------------------------------------
Material::Material() :
m_Color( Color( 0.2f, 0.2f, 0.2f ) ),
m_Refl( 0 ), m_Diff( 0.2f ), m_Spec( 0.8f ), m_RIndex( 1.5f )
{
}
// -----------------------------------------------------------
// Sphere primitive methods
// -----------------------------------------------------------
int Sphere::Intersect( Ray& a_Ray, float& a_Dist )
{
vector3 v = a_Ray.GetOrigin() - m_Centre;
float b = -DOT( v, a_Ray.GetDirection() );
float det = (b * b) - DOT( v, v ) + m_SqRadius;
int retval = MISS;
if (det > 0)
{
det = sqrtf( det );
float i1 = b - det;
float i2 = b + det;
if (i2 > 0)
{
if (i1 < 0)
{
if (i2 < a_Dist)
{
a_Dist = i2;
retval = INPRIM;
}
}
else
{
if (i1 < a_Dist)
{
a_Dist = i1;
retval = HIT;
}
}
}
}
return retval;
}
bool Sphere::IntersectBox( aabb& a_Box )
{
float dmin = 0;
vector3 v1 = a_Box.GetPos(), v2 = a_Box.GetPos() + a_Box.GetSize();
if (m_Centre.x < v1.x)
{
dmin = dmin + (m_Centre.x - v1.x) * (m_Centre.x - v1.x);
}
else if (m_Centre.x > v2.x)
{
dmin = dmin + (m_Centre.x - v2.x) * (m_Centre.x - v2.x);
}
if (m_Centre.y < v1.y)
{
dmin = dmin + (m_Centre.y - v1.y) * (m_Centre.y - v1.y);
}
else if (m_Centre.y > v2.y)
{
dmin = dmin + (m_Centre.y - v2.y) * (m_Centre.y - v2.y);
}
if (m_Centre.z < v1.z)
{
dmin = dmin + (m_Centre.z - v1.z) * (m_Centre.z - v1.z);
}
else if (m_Centre.z > v2.z)
{
dmin = dmin + (m_Centre.z - v2.z) * (m_Centre.z - v2.z);
}
return (dmin <= m_SqRadius);
}
aabb Sphere::GetAABB()
{
vector3 size( m_Radius, m_Radius, m_Radius );
return aabb( m_Centre - size, size * 2 );
}
// -----------------------------------------------------------
// Plane primitive class implementation
// -----------------------------------------------------------
int PlanePrim::Intersect( Ray& a_Ray, float& a_Dist )
{
float d = DOT( m_Plane.N, a_Ray.GetDirection() );
if (d < 0)
{
float dist = -(DOT( m_Plane.N, a_Ray.GetOrigin() ) + m_Plane.D) / d;
if (dist < a_Dist)
{
a_Dist = dist;
return HIT;
}
}
return MISS;
}
bool PlanePrim::IntersectBox( aabb& a_Box )
{
vector3 v[2];
v[0] = a_Box.GetPos(), v[1] = a_Box.GetPos() + a_Box.GetSize();
for ( int side1 = 0, side2 = 0, i = 0; i < 8; i++ )
{
vector3 p( v[i & 1].x, v[(i >> 1) & 1].y, v[(i >> 2) & 1].z );
if ((DOT( p, m_Plane.N ) + m_Plane.D) < 0) side1++; else side2++;
}
if ((side1 == 0) || (side2 == 0)) return false; else return true;
}
vector3 PlanePrim::GetNormal( vector3& a_Pos )
{
return m_Plane.N;
}
// -----------------------------------------------------------
// Axis aligned box primitive class implementation
// -----------------------------------------------------------
Box::Box() :
m_Box( vector3( 0, 0, 0 ), vector3( 0, 0, 0 ) ),
m_Grid( 0 )
{
}
Box::Box( aabb& a_Box ) :
m_Box( a_Box ),
m_Grid( 0 )
{
}
int Box::Intersect( Ray& a_Ray, float& a_Dist )
{
m_RayID = a_Ray.GetID();
float dist[6];
vector3 ip[6], d = a_Ray.GetDirection(), o = a_Ray.GetOrigin();
bool retval = MISS;
for ( int i = 0; i < 6; i++ ) dist[i] = -1;
vector3 v1 = m_Box.GetPos(), v2 = m_Box.GetPos() + GetSize();
if (d.x)
{
float rc = 1.0f / d.x;
dist[0] = (v1.x - o.x) * rc;
dist[3] = (v2.x - o.x) * rc;
}
if (d.y)
{
float rc = 1.0f / d.y;
dist[1] = (v1.y - o.y) * rc;
dist[4] = (v2.y - o.y) * rc;
}
if (d.z)
{
float rc = 1.0f / d.z;
dist[2] = (v1.z - o.z) * rc;
dist[5] = (v2.z - o.z) * rc;
}
for ( i = 0; i < 6; i++ ) if (dist[i] > 0)
{
ip[i] = o + dist[i] * d;
if ((ip[i].x > (v1.x - EPSILON)) && (ip[i].x < (v2.x + EPSILON)) &&
(ip[i].y > (v1.y - EPSILON)) && (ip[i].y < (v2.y + EPSILON)) &&
(ip[i].z > (v1.z - EPSILON)) && (ip[i].z < (v2.z + EPSILON)))
{
if (dist[i] < a_Dist)
{
a_Dist = dist[i];
retval = HIT;
}
}
}
return retval;
}
vector3 Box::GetNormal( vector3& a_Pos )
{
float dist[6];
dist[0] = (float)fabs( m_Box.GetSize().x - m_Box.GetPos().x );
dist[1] = (float)fabs( m_Box.GetSize().x + m_Box.GetSize().x - m_Box.GetPos().x );
dist[2] = (float)fabs( m_Box.GetSize().y - m_Box.GetPos().y );
dist[3] = (float)fabs( m_Box.GetSize().y + m_Box.GetSize().y - m_Box.GetPos().y );
dist[4] = (float)fabs( m_Box.GetSize().z - m_Box.GetPos().z );
dist[5] = (float)fabs( m_Box.GetSize().z + m_Box.GetSize().z - m_Box.GetPos().z );
int best = 0;
float bdist = dist[0];
for ( int i = 1 ; i < 6; i++ ) if (dist[i] < bdist)
{
bdist = dist[i];
best = i;
}
if (best == 0) return vector3( -1, 0, 0 );
else if (best == 1) return vector3( 1, 0, 0 );
else if (best == 2) return vector3( 0, -1, 0 );
else if (best == 3) return vector3( 0, 1, 0 );
else if (best == 4) return vector3( 0, 0, -1 );
else return vector3( 0, 0, 1 );
}
void Box::Light( bool a_Light )
{
m_Light = a_Light;
if (!m_Grid)
{
m_Grid = new float[32];
m_Grid[ 0] = 1, m_Grid[ 1] = 2;
m_Grid[ 2] = 3, m_Grid[ 3] = 3;
m_Grid[ 4] = 2, m_Grid[ 5] = 0;
m_Grid[ 6] = 0, m_Grid[ 7] = 1;
m_Grid[ 8] = 2, m_Grid[ 9] = 3;
m_Grid[10] = 0, m_Grid[11] = 3;
m_Grid[12] = 0, m_Grid[13] = 0;
m_Grid[14] = 2, m_Grid[15] = 2;
m_Grid[16] = 3, m_Grid[17] = 1;
m_Grid[18] = 1, m_Grid[19] = 3;
m_Grid[20] = 1, m_Grid[21] = 0;
m_Grid[22] = 3, m_Grid[23] = 2;
m_Grid[24] = 2, m_Grid[25] = 1;
m_Grid[26] = 3, m_Grid[27] = 0;
m_Grid[28] = 1, m_Grid[29] = 1;
m_Grid[30] = 0, m_Grid[31] = 2;
for ( int i = 0; i < 16; i++ )
{
m_Grid[i * 2] = m_Grid[i * 2] * m_Box.GetSize().x / 4 + m_Box.GetPos().x;
m_Grid[i * 2 + 1] = m_Grid[i * 2 + 1] * m_Box.GetSize().z / 4 + m_Box.GetPos().z;
}
}
}
// -----------------------------------------------------------
// Scene class implementation
// -----------------------------------------------------------
Scene::~Scene()
{
delete m_Primitive;
}
void Scene::InitScene()
{
m_Primitive = new Primitive*[500];
// ground plane
m_Primitive[0] = new PlanePrim( vector3( 0, 1, 0 ), 4.4f );
m_Primitive[0]->SetName( "plane" );
m_Primitive[0]->GetMaterial()->SetReflection( 0.0f );
m_Primitive[0]->GetMaterial()->SetRefraction( 0.0f );
m_Primitive[0]->GetMaterial()->SetDiffuse( 1.0f );
m_Primitive[0]->GetMaterial()->SetColor( Color( 0.4f, 0.3f, 0.3f ) );
// big sphere
m_Primitive[1] = new Sphere( vector3( 2, 0.8f, 3 ), 2.5f );
m_Primitive[1]->SetName( "big sphere" );
m_Primitive[1]->GetMaterial()->SetReflection( 0.2f );
m_Primitive[1]->GetMaterial()->SetRefraction( 0.8f );
m_Primitive[1]->GetMaterial()->SetRefrIndex( 1.3f );
m_Primitive[1]->GetMaterial()->SetColor( Color( 0.7f, 0.7f, 1.0f ) );
// small sphere
m_Primitive[2] = new Sphere( vector3( -5.5f, -0.5, 7 ), 2 );
m_Primitive[2]->SetName( "small sphere" );
m_Primitive[2]->GetMaterial()->SetReflection( 0.5f );
m_Primitive[2]->GetMaterial()->SetRefraction( 0.0f );
m_Primitive[2]->GetMaterial()->SetRefrIndex( 1.3f );
m_Primitive[2]->GetMaterial()->SetDiffuse( 0.1f );
m_Primitive[2]->GetMaterial()->SetColor( Color( 0.7f, 0.7f, 1.0f ) );
// light source 1
m_Primitive[3] = new Sphere( vector3( 0, 5, 5 ), 0.1f );
m_Primitive[3]->Light( true );
m_Primitive[3]->GetMaterial()->SetColor( Color( 0.4f, 0.4f, 0.4f ) );
// light source 2
m_Primitive[4] = new Sphere( vector3( -3, 5, 1 ), 0.1f );
m_Primitive[4]->Light( true );
m_Primitive[4]->GetMaterial()->SetColor( Color( 0.6f, 0.6f, 0.8f ) );
// extra sphere
m_Primitive[5] = new Sphere( vector3( -1.5f, -3.8f, 1 ), 1.5f );
m_Primitive[5]->SetName( "extra sphere" );
m_Primitive[5]->GetMaterial()->SetReflection( 0.0f );
m_Primitive[5]->GetMaterial()->SetRefraction( 0.8f );
m_Primitive[5]->GetMaterial()->SetColor( Color( 1.0f, 0.4f, 0.4f ) );
// back plane
m_Primitive[6] = new PlanePrim( vector3( 0.4f, 0, -1 ), 12 );
m_Primitive[6]->SetName( "back plane" );
m_Primitive[6]->GetMaterial()->SetReflection( 0.0f );
m_Primitive[6]->GetMaterial()->SetRefraction( 0.0f );
m_Primitive[6]->GetMaterial()->SetSpecular( 0 );
m_Primitive[6]->GetMaterial()->SetDiffuse( 0.6f );
m_Primitive[6]->GetMaterial()->SetColor( Color( 0.5f, 0.3f, 0.5f ) );
// ceiling plane
m_Primitive[7] = new PlanePrim( vector3( 0, -1, 0 ), 7.4f );
m_Primitive[7]->SetName( "back plane" );
m_Primitive[7]->GetMaterial()->SetReflection( 0.0f );
m_Primitive[7]->GetMaterial()->SetRefraction( 0.0f );
m_Primitive[7]->GetMaterial()->SetSpecular( 0 );
m_Primitive[7]->GetMaterial()->SetDiffuse( 0.5f );
m_Primitive[7]->GetMaterial()->SetColor( Color( 0.4f, 0.7f, 0.7f ) );
// grid
int prim = 8;
for ( int x = 0; x < 8; x++ ) for ( int y = 0; y < 7; y++ )
{
m_Primitive[prim] = new Sphere( vector3( -4.5f + x * 1.5f, -4.3f + y * 1.5f, 10 ), 0.3f );
m_Primitive[prim]->SetName( "grid sphere" );
m_Primitive[prim]->GetMaterial()->SetReflection( 0 );
m_Primitive[prim]->GetMaterial()->SetRefraction( 0 );
m_Primitive[prim]->GetMaterial()->SetSpecular( 0.6f );
m_Primitive[prim]->GetMaterial()->SetDiffuse( 0.6f );
m_Primitive[prim]->GetMaterial()->SetColor( Color( 0.3f, 1.0f, 0.4f ) );
prim++;
}
for ( x = 0; x < 8; x++ ) for ( int y = 0; y < 8; y++ )
{
m_Primitive[prim] = new Sphere( vector3( -4.5f + x * 1.5f, -4.3f, 10.0f - y * 1.5f ), 0.3f );
m_Primitive[prim]->SetName( "grid sphere" );
m_Primitive[prim]->GetMaterial()->SetReflection( 0 );
m_Primitive[prim]->GetMaterial()->SetRefraction( 0 );
m_Primitive[prim]->GetMaterial()->SetSpecular( 0.6f );
m_Primitive[prim]->GetMaterial()->SetDiffuse( 0.6f );
m_Primitive[prim]->GetMaterial()->SetColor( Color( 0.3f, 1.0f, 0.4f ) );
prim++;
}
for ( x = 0; x < 16; x++ ) for ( int y = 0; y < 8; y++ )
{
m_Primitive[prim] = new Sphere( vector3( -8.5f + x * 1.5f, 4.3f, 10.0f - y ), 0.3f );
m_Primitive[prim]->SetName( "grid sphere" );
m_Primitive[prim]->GetMaterial()->SetReflection( 0 );
m_Primitive[prim]->GetMaterial()->SetRefraction( 0 );
m_Primitive[prim]->GetMaterial()->SetSpecular( 0.6f );
m_Primitive[prim]->GetMaterial()->SetDiffuse( 0.6f );
m_Primitive[prim]->GetMaterial()->SetColor( Color( 0.3f, 1.0f, 0.4f ) );
prim++;
}
// set number of primitives
m_Primitives = prim;
// build the regular gird
BuildGrid();
}
void Scene::BuildGrid()
{
// initialize regular grid
m_Grid = new ObjectList*[GRIDSIZE * GRIDSIZE * GRIDSIZE];
memset( m_Grid, 0, GRIDSIZE * GRIDSIZE * GRIDSIZE * 4 );
vector3 p1(-14, -5, -6), p2( 14, 8, 30 );
// calculate cell width, height and depth
float dx = (p2.x - p1.x) / GRIDSIZE, dx_reci = 1.0f / dx;
float dy = (p2.y - p1.y) / GRIDSIZE, dy_reci = 1.0f / dy;
float dz = (p2.z - p1.z) / GRIDSIZE, dz_reci = 1.0f / dz;
m_Extends = aabb( p1, p2 - p1 );
m_Light = new Primitive*[MAXLIGHTS];
m_Lights = 0;
// store primitives in the grid cells
for ( int p = 0; p < m_Primitives; p++ )
{
if (m_Primitive[p]->IsLight()) m_Light[m_Lights++] = m_Primitive[p];
aabb bound = m_Primitive[p]->GetAABB();
vector3 bv1 = bound.GetPos(), bv2 = bound.GetPos() + bound.GetSize();
// find out which cells could contain the primitive (based on aabb)
int x1 = (int)((bv1.x - p1.x) * dx_reci), x2 = (int)((bv2.x - p1.x) * dx_reci) + 1;
x1 = (x1 < 0)?0:x1, x2 = (x2 > (GRIDSIZE - 1))?GRIDSIZE - 1:x2;
int y1 = (int)((bv1.y - p1.y) * dy_reci), y2 = (int)((bv2.y - p1.y) * dy_reci) + 1;
y1 = (y1 < 0)?0:y1, y2 = (y2 > (GRIDSIZE - 1))?GRIDSIZE - 1:y2;
int z1 = (int)((bv1.z - p1.z) * dz_reci), z2 = (int)((bv2.z - p1.z) * dz_reci) + 1;
z1 = (z1 < 0)?0:z1, z2 = (z2 > (GRIDSIZE - 1))?GRIDSIZE - 1:z2;
// loop over candidate cells
for ( int x = x1; x < x2; x++ ) for ( int y = y1; y < y2; y++ ) for ( int z = z1; z < z2; z++ )
{
// construct aabb for current cell
int idx = x + y * GRIDSIZE + z * GRIDSIZE * GRIDSIZE;
vector3 pos( p1.x + x * dx, p1.y + y * dy, p1.z + z * dz );
aabb cell( pos, vector3( dx, dy, dz ) );
// do an accurate aabb / primitive intersection test
if (m_Primitive[p]->IntersectBox( cell ))
{
// object intersects cell; add to object list
ObjectList* l = new ObjectList();
l->SetPrimitive( m_Primitive[p] );
l->SetNext( m_Grid[idx] );
m_Grid[idx] = l;
}
}
}
}
}; // namespace Raytracer