Subversion Repositories gelsvn

Rev

Rev 510 | Rev 595 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#ifndef __GEOMETRY_RAY_H__
#define __GEOMETRY_RAY_H__

#include "../CGLA/Vec3i.h"
#include "../CGLA/Vec3f.h"
#include "TriMesh.h"
#include "Material.h"

namespace Geometry 
{
    const double d_eps = 1.0e-12;
    const float f_eps = 1.0e-6f;

    struct Ray 
    {
        // Constructor
        Ray() 
          : origin(0.0f), direction(0.0f), hit_pos(0.0f), hit_normal(0.0f), 
            has_hit(false), dist(CGLA::BIG), ior(1.0f), u(0.0f), v(0.0f), 
            hit_object(0), trace_depth(0), inside(false), did_hit_diffuse(false)
        { }

        Ray(const CGLA::Vec3f& _origin, const CGLA::Vec3f& _direction) 
          : origin(_origin), direction(_direction), hit_pos(0.0f), hit_normal(0.0f), 
            has_hit(false), dist(CGLA::BIG), ior(1.0f), u(0.0f), v(0.0f), 
            hit_object(0), trace_depth(0), inside(false), did_hit_diffuse(false)
        { }

        CGLA::Vec3f origin;
        CGLA::Vec3f direction;
        CGLA::Vec3f hit_pos;
        CGLA::Vec3f hit_normal;

        bool has_hit; // Did the ray hit an object
        bool inside;  // Is the ray inside an object
        bool did_hit_diffuse;

        double dist;  // Distance from origin to current intersection
        float ior;    // Current index of refraction for media
        float u, v;   // uv-coordinates on current surface

        int trace_depth;  // Current recursion number
        size_t hit_face_id;
        int id;

        const TriMesh* hit_object;

        const Material* get_hit_material() const
        {
          if(!hit_object)
            return 0;
          return &hit_object->materials[hit_object->mat_idx[hit_face_id]];
        }

        void reset()
        {
            dist = CGLA::BIG; 
            hit_object = 0;
            u=0.0f;
            v=0.0f;
            has_hit=false;      
        }

        void compute_position()
        {
            hit_pos = origin + dist*direction;      
        }

        void compute_normal()
        {
            const CGLA::Vec3i& face = hit_object->normals.face(hit_face_id);
            const CGLA::Vec3f& normal0 = hit_object->normals.vertex(face[0]);
            const CGLA::Vec3f& normal1 = hit_object->normals.vertex(face[1]);
            const CGLA::Vec3f& normal2 = hit_object->normals.vertex(face[2]);
            hit_normal = normalize(normal0*(1 - u - v) + normal1*u + normal2*v);      
        }

        void reflect(const CGLA::Vec3f &normal)
        {
            assert(dot(direction, normal) < 0.0f);
            direction = normal*2.0f*dot(-direction,normal) + direction;      
        }

        void refract(const CGLA::Vec3f& normal, float new_ior)
        {
            float ref_ratio = ior/new_ior;
            float cos_N_I = dot(normal, direction);
            CGLA::Vec3f norm(normal);

            if(cos_N_I > 0.0f)
            {
                norm = -norm;
                cos_N_I = dot(norm, direction);
            }

            float selector = 1+(ref_ratio*ref_ratio)*(cos_N_I*cos_N_I - 1);

            if(selector > 0.0f) 
            {
                direction = norm*(ref_ratio*(-cos_N_I) - sqrt(selector)) 
                            + direction*ref_ratio;
                ior = new_ior;
            } 
            else 
                // Total internal reflection.
                reflect(normal);
        }

        bool cond_set_parameter(float t, float _u, float _v, const Geometry::TriMesh* mesh, size_t idx)
        {
            if(t < dist)
            {
                dist = t;
                u = _u;
                v = _v;
                hit_object = mesh;
                hit_face_id = idx;
                has_hit = true;
                return true;
            }
            return false;
        }
    };
}
#endif