2022-10-16 19:24:37 -04:00
/*
* Created by Brett Terpstra 6920201 on 16 / 10 / 22.
* Copyright ( c ) 2022 Brett Terpstra . All Rights Reserved .
*/
# include <world.h>
2022-10-17 19:16:10 -04:00
# include <raytracing.h>
2022-10-16 19:24:37 -04:00
namespace Raytracing {
World : : ~ World ( ) {
2022-10-17 19:16:10 -04:00
for ( auto * p : objects )
delete ( p ) ;
for ( const auto & p : materials )
delete ( p . second ) ;
2022-10-19 00:43:16 -04:00
//delete(bvhTree);
2022-10-16 19:24:37 -04:00
}
2022-10-17 19:16:10 -04:00
HitData SphereObject : : checkIfHit ( const Ray & ray , PRECISION_TYPE min , PRECISION_TYPE max ) const {
2022-10-17 00:29:34 -04:00
PRECISION_TYPE radiusSquared = radius * radius ;
// move the ray to be with respects to the sphere
2022-10-18 23:11:51 -04:00
Vec4 RayWRTSphere = ray . getStartingPoint ( ) - position ;
2022-10-17 00:29:34 -04:00
// now determine the discriminant for the quadratic formula for the function of line sphere intercept
PRECISION_TYPE a = ray . getDirection ( ) . lengthSquared ( ) ;
2022-10-18 23:11:51 -04:00
PRECISION_TYPE b = Raytracing : : Vec4 : : dot ( RayWRTSphere , ray . getDirection ( ) ) ;
2022-10-17 00:29:34 -04:00
PRECISION_TYPE c = RayWRTSphere . lengthSquared ( ) - radiusSquared ;
// > 0: the hit has two roots, meaning we hit both sides of the sphere
// = 0: the ray has one root, we hit the edge of the sphere
// < 0: ray isn't inside the sphere.
PRECISION_TYPE discriminant = b * b - ( a * c ) ;
// < 0: ray isn't inside the sphere. Don't need to bother calculating the roots.
if ( discriminant < 0 )
2022-10-18 23:11:51 -04:00
return { false , Vec4 ( ) , Vec4 ( ) , 0 } ;
2022-10-17 00:29:34 -04:00
// now we have to find the root which exists inside our range [min,max]
auto root = ( - b - std : : sqrt ( discriminant ) ) / a ;
// if the first root isn't in our range
if ( root < min | | root > max ) {
// check the second root
root = ( - b + std : : sqrt ( discriminant ) ) / a ;
if ( root < min | | root > max ) {
// if the second isn't in the range then we also must return false.
2022-10-18 23:11:51 -04:00
return { false , Vec4 ( ) , Vec4 ( ) , 0 } ;
2022-10-17 00:29:34 -04:00
}
}
// the hit point is where the ray is when extended to the root
auto RayAtRoot = ray . along ( root ) ;
// The normal of a sphere is just the point of the hit minus the center position
auto normal = ( RayAtRoot - position ) . normalize ( ) ;
/*if (Raytracing::vec4::dot(ray.getDirection(), normal) > 0.0) {
tlog < < " ray inside sphere \n " ;
} else
tlog < < " ray outside sphere \n " ;
*/
return { true , RayAtRoot , normal , root } ;
}
2022-10-17 19:16:10 -04:00
std : : pair < HitData , Object * > World : : checkIfHit ( const Ray & ray , PRECISION_TYPE min , PRECISION_TYPE max ) const {
2022-10-19 00:43:16 -04:00
/*if (bvhTree != nullptr){
2022-10-18 23:11:51 -04:00
auto hResult = HitData { false , Vec4 ( ) , Vec4 ( ) , max } ;
Object * objPtr = nullptr ;
auto intersected = bvhTree - > rayIntersect ( ray , min , max ) ;
//dlog << "Intersections " << intersected.size() << " " << ray << "\n";
for ( auto * ptr : intersected ) {
auto cResult = ptr - > checkIfHit ( ray , min , hResult . length ) ;
if ( cResult . hit ) {
hResult = cResult ;
objPtr = ptr ;
}
}
// after we check the BVH, we have to check for other missing objects
// since stuff like spheres currently don't have AABB and AABB isn't a requirement
// for the object class (to be assigned)
for ( auto * obj : bvhTree - > noAABBObjects ) {
// check up to the point of the last closest hit,
// will give the closest object's hit result
auto cResult = obj - > checkIfHit ( ray , min , hResult . length ) ;
if ( cResult . hit ) {
hResult = cResult ;
objPtr = obj ;
}
2022-10-17 19:16:10 -04:00
}
2022-10-18 23:11:51 -04:00
return { hResult , objPtr } ;
2022-10-19 00:43:16 -04:00
} else { */
2022-10-18 23:11:51 -04:00
// rejection algo without using a binary space partitioning data structure
auto hResult = HitData { false , Vec4 ( ) , Vec4 ( ) , max } ;
Object * objPtr = nullptr ;
for ( auto * obj : objects ) {
// check up to the point of the last closest hit,
// will give the closest object's hit result
auto cResult = obj - > checkIfHit ( ray , min , hResult . length ) ;
if ( cResult . hit ) {
hResult = cResult ;
objPtr = obj ;
}
}
return { hResult , objPtr } ;
2022-10-19 00:43:16 -04:00
//}
2022-10-18 23:11:51 -04:00
}
void World : : generateBVH ( ) {
2022-10-19 00:43:16 -04:00
//bvhTree = new BVHTree(objects);
2022-10-17 19:16:10 -04:00
}
ScatterResults DiffuseMaterial : : scatter ( const Ray & ray , const HitData & hitData ) const {
2022-10-18 23:11:51 -04:00
Vec4 newRay = hitData . normal + Raytracing : : Raycaster : : randomUnitVector ( ) . normalize ( ) ;
2022-10-17 19:16:10 -04:00
// rays that are close to zero are liable to floating point precision errors
if ( newRay . x ( ) < EPSILON & & newRay . y ( ) < EPSILON & & newRay . z ( ) < EPSILON & & newRay . w ( ) < EPSILON )
newRay = hitData . normal ;
return { true , Ray { hitData . hitPoint , newRay } , getBaseColor ( ) } ;
}
ScatterResults MetalMaterial : : scatter ( const Ray & ray , const HitData & hitData ) const {
// create a ray reflection
2022-10-18 23:11:51 -04:00
Vec4 newRay = reflect ( ray . getDirection ( ) . normalize ( ) , hitData . normal ) ;
2022-10-17 19:16:10 -04:00
// make sure our reflected ray is outside the sphere and doesn't point inwards
2022-10-18 23:11:51 -04:00
bool shouldReflect = Vec4 : : dot ( newRay , hitData . normal ) > 0 ;
2022-10-17 19:16:10 -04:00
return { shouldReflect , Ray { hitData . hitPoint , newRay } , getBaseColor ( ) } ;
}
ScatterResults BrushedMetalMaterial : : scatter ( const Ray & ray , const HitData & hitData ) const {
// create a ray reflection
2022-10-18 23:11:51 -04:00
Vec4 newRay = reflect ( ray . getDirection ( ) . normalize ( ) , hitData . normal ) ;
2022-10-17 19:16:10 -04:00
// make sure our reflected ray is outside the sphere and doesn't point inwards
2022-10-18 23:11:51 -04:00
bool shouldReflect = Vec4 : : dot ( newRay , hitData . normal ) > 0 ;
2022-10-17 19:16:10 -04:00
return { shouldReflect , Ray { hitData . hitPoint , newRay + Raycaster : : randomUnitVector ( ) * fuzzyness } , getBaseColor ( ) } ;
}
2022-10-18 23:11:51 -04:00
static HitData checkIfTriangleGotHit ( const Triangle & theTriangle , const Vec4 & position , const Ray & ray , PRECISION_TYPE min , PRECISION_TYPE max ) {
2022-10-17 19:16:10 -04:00
// Möller– Trumbore intersection algorithm
2022-10-18 00:44:49 -04:00
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
2022-10-18 23:11:51 -04:00
Vec4 edge1 , edge2 , h , s , q ;
2022-10-17 19:16:10 -04:00
PRECISION_TYPE a , f , u , v ;
edge1 = ( theTriangle . vertex2 + position ) - ( theTriangle . vertex1 + position ) ;
edge2 = ( theTriangle . vertex3 + position ) - ( theTriangle . vertex1 + position ) ;
2022-10-18 23:11:51 -04:00
h = Vec4 : : cross ( ray . getDirection ( ) , edge2 ) ;
a = Vec4 : : dot ( edge1 , h ) ;
2022-10-17 19:16:10 -04:00
if ( a > - EPSILON & & a < EPSILON )
2022-10-18 23:11:51 -04:00
return { false , Vec4 ( ) , Vec4 ( ) , 0 } ; //parallel to triangle
2022-10-17 19:16:10 -04:00
f = 1.0 / a ;
s = ray . getStartingPoint ( ) - ( theTriangle . vertex1 + position ) ;
2022-10-18 23:11:51 -04:00
u = f * Vec4 : : dot ( s , h ) ;
2022-10-17 19:16:10 -04:00
if ( u < 0.0 | | u > 1.0 )
2022-10-18 23:11:51 -04:00
return { false , Vec4 ( ) , Vec4 ( ) , 0 } ;
2022-10-17 19:16:10 -04:00
2022-10-18 23:11:51 -04:00
q = Vec4 : : cross ( s , edge1 ) ;
v = f * Vec4 : : dot ( ray . getDirection ( ) , q ) ;
2022-10-17 19:16:10 -04:00
if ( v < 0.0 | | u + v > 1.0 )
2022-10-18 23:11:51 -04:00
return { false , Vec4 ( ) , Vec4 ( ) , 0 } ;
2022-10-17 19:16:10 -04:00
// At this stage we can compute t to find out where the intersection point is on the line.
2022-10-18 23:11:51 -04:00
PRECISION_TYPE t = f * Vec4 : : dot ( edge2 , q ) ;
2022-10-19 00:43:16 -04:00
// keep t in reasonable bounds, ensuring we respect depth
if ( t > EPSILON & & t > = min & & t < = max ) {
2022-10-17 19:16:10 -04:00
// ray intersects
2022-10-18 23:11:51 -04:00
Vec4 rayIntersectionPoint = ray . along ( t ) ;
Vec4 normal ;
2022-10-18 00:44:49 -04:00
// normal = theTriangle.findClosestNormal(rayIntersectionPoint - position);
if ( theTriangle . hasNormals ) // returning the closest normal is extra computation when n1 would likely be fine.
2022-10-17 19:16:10 -04:00
normal = theTriangle . normal1 ;
else {
// standard points to normal algorithm but using already computed edges
2022-10-18 23:11:51 -04:00
normal = Vec4 { edge1 . y ( ) * edge2 . z ( ) , edge1 . z ( ) * edge2 . x ( ) , edge1 . x ( ) * edge2 . y ( ) } -
Vec4 { edge1 . z ( ) * edge2 . y ( ) , edge1 . x ( ) * edge2 . z ( ) , edge1 . y ( ) * edge2 . x ( ) } ;
2022-10-17 19:16:10 -04:00
}
return { true , rayIntersectionPoint , normal , t } ;
2022-10-16 19:24:37 -04:00
}
2022-10-18 23:11:51 -04:00
return { false , Vec4 ( ) , Vec4 ( ) , 0 } ;
2022-10-16 19:24:37 -04:00
}
2022-10-18 00:44:49 -04:00
HitData TriangleObject : : checkIfHit ( const Ray & ray , PRECISION_TYPE min , PRECISION_TYPE max ) const {
return checkIfTriangleGotHit ( theTriangle , position , ray , min , max ) ;
}
HitData ModelObject : : checkIfHit ( const Ray & ray , PRECISION_TYPE min , PRECISION_TYPE max ) const {
2022-10-19 00:43:16 -04:00
auto hResult = HitData { false , Vec4 ( ) , Vec4 ( ) , max } ;
2022-10-18 00:44:49 -04:00
for ( const Triangle & t : triangles ) {
auto cResult = checkIfTriangleGotHit ( t , position , ray , min , hResult . length ) ;
if ( cResult . hit )
hResult = cResult ;
2022-10-19 00:43:16 -04:00
}
// if you've made it to this point this will be in the parallel step version
// I've run out of time to mess with this
// and I hate that he made this due in the middle of exam / assignment season
// it's really hard for me to get in there and obsess over this
// when i got 4 other classes cram-ing as much useless bullshit into the next week and a half for no reason other than
// "hahahahha I rambled about nonsense in class for the last few weeks let's test your knowledge with a tedious assignment!!! woooooo!!!!!"
// this is honestly the only good 3rd year class I've taken so far
// Only if I had more time. THINK OF THE GRAPHS I COULD'VE MADE!!!
// I had plans (and still do) to make graphs of every little performance bottleneck
// but it's impossible when you got all this stuff due, C++ decides it hates you and wants to segfault for dumb reasons, and the program takes 20 MINUTES TO RUN!
// would've been able to get the GUI stuff in and fix the BVH by visualizing the bounding boxes
/*auto hResult = HitData{false, Vec4(), Vec4(), max};
2022-10-18 23:11:51 -04:00
auto intersected = tree - > rayIntersect ( ray , min , max ) ;
for ( auto t : intersected ) {
// apparently this kind of casting is okay
// which makes sense since the actual data behind it is a empty object
// just this is really bad and im too annoyed to figure out a better way. TODO:.
auto cResult = checkIfTriangleGotHit ( ( ( EmptyObject * ) ( t ) ) - > tri , position , ray , min , hResult . length ) ;
if ( cResult . hit )
hResult = cResult ;
2022-10-19 00:43:16 -04:00
} */
2022-10-18 23:11:51 -04:00
2022-10-18 00:44:49 -04:00
return hResult ;
}
2022-10-16 19:24:37 -04:00
}