Skip to content

Commit 1b33c70

Browse files
Merge pull request #15 from EdwardPalmer99/EdwardPalmer99/feature/CSG
Implement Constructive Solid Geometry (CSG)
2 parents 1a18de5 + 470e62b commit 1b33c70

26 files changed

Lines changed: 1051 additions & 140 deletions

examples/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ cc_binary(
1515
"//lib:cphoton"
1616
],
1717
visibility = ["//visibility:private"]
18-
)
18+
)

examples/CSG/BUILD.bazel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
cc_binary(
2+
name = "hemisphere",
3+
srcs = ["Hemisphere.cpp"],
4+
deps = [
5+
"//lib:cphoton"
6+
],
7+
visibility = ["//visibility:private"]
8+
)

examples/CSG/Hemisphere.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @file Spheres.C
3+
* @author Edward Palmer
4+
* @date 2025-02-22
5+
*
6+
* @copyright Copyright (c) 2025
7+
*
8+
*/
9+
10+
extern "C"
11+
{
12+
#include "engine/RenderSettings.h"
13+
}
14+
15+
#include "engine/PhotonEngine.hpp"
16+
#include "engine/Scene.hpp"
17+
#include "engine/primitives/CSGNode.hpp"
18+
#include "engine/primitives/Plane.hpp"
19+
#include "engine/primitives/Primitive.hpp"
20+
#include "engine/primitives/Sphere.hpp"
21+
22+
int main(int argc, const char *argv[])
23+
{
24+
gRenderSettings.pixelsWide = 500;
25+
gRenderSettings.pixelsHigh = 500;
26+
27+
parseCLIOptions(argc, argv);
28+
29+
// Create the camera:
30+
const double aspectRatio = ((double)gRenderSettings.pixelsWide / (double)gRenderSettings.pixelsHigh);
31+
Camera camera(45.0, aspectRatio, 1, 0, point3(0, 4, 4), point3(0, 1, 0));
32+
33+
Primitive *sphere1 = new Sphere(point3(0, 1, 0), 1, makeMetal(makeSolidTexture(color3(0, 1, 0)), 0));
34+
Primitive *sphere2 = new Sphere(point3(0, 1, 0.5), 1, makeMetal(makeSolidTexture(color3(0, 1, 0)), 0));
35+
36+
Primitive *plane = new Plane(point3(0, 0, 0), point3(0, 1, 0), makeLambertianWithColor(color3(0.1, 0.1, 0.1)));
37+
38+
Primitive *CSG = new CSGNode(sphere1, sphere2, CSGNode::CSGDifference);
39+
40+
Scene scene;
41+
scene.addObject(CSG);
42+
scene.addObject(plane);
43+
44+
PhotonEngine engine(gRenderSettings.pixelsWide, gRenderSettings.pixelsHigh);
45+
46+
PPMImage *outputImage = engine.render(&scene, &camera);
47+
writeBinary16BitPPMImage(outputImage, gRenderSettings.outputPath);
48+
freePPMImage(outputImage);
49+
50+
return 0;
51+
}

lib/engine/AABB.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* @file AABB.cpp
3+
* @author Edward Palmer
4+
* @date 2025-03-03
5+
*
6+
* @copyright Copyright (c) 2025
7+
*
8+
*/
9+
10+
#include "AABB.hpp"
11+
12+
#define swap(val1, val2) \
13+
({ \
14+
double temp = (val1); \
15+
(val1) = (val2); \
16+
(val2) = temp; \
17+
})
18+
19+
20+
AABB::AABB()
21+
{
22+
reset();
23+
}
24+
25+
26+
AABB::AABB(Point3 min_, Point3 max_) : min(min_), max(max_)
27+
{
28+
}
29+
30+
31+
void AABB::reset()
32+
{
33+
min = point3(INFINITY, INFINITY, INFINITY);
34+
max = point3(-INFINITY, -INFINITY, -INFINITY);
35+
}
36+
37+
38+
void AABB::addPoint(Point3 pt)
39+
{
40+
if (pt.x < min.x) min.x = pt.x;
41+
if (pt.x > max.x) max.x = pt.x;
42+
43+
if (pt.y < min.y) min.y = pt.y;
44+
if (pt.y > max.y) max.y = pt.y;
45+
46+
if (pt.z < min.z) min.z = pt.z;
47+
if (pt.z > max.z) max.z = pt.z;
48+
}
49+
50+
51+
AABB AABB::addBoundingBoxes(const AABB &box0, const AABB &box1)
52+
{
53+
Point3 newMin =
54+
point3(std::min(box0.min.x, box1.min.x), std::min(box0.min.y, box1.min.y), std::min(box0.min.z, box1.min.z));
55+
Point3 newMax =
56+
point3(std::max(box0.max.x, box1.max.x), std::max(box0.max.y, box1.max.y), std::max(box0.max.z, box1.max.z));
57+
58+
return AABB(newMin, newMax);
59+
}
60+
61+
62+
AABB AABB::operator+(const AABB &other)
63+
{
64+
return addBoundingBoxes(*this, other);
65+
}
66+
67+
68+
bool AABB::hit(Ray *ray, double tmin, double tmax)
69+
{
70+
Point3 origin = ray->origin;
71+
72+
// Now test against x-direction.
73+
double invD = 1.0 / ray->direction.x;
74+
double t0 = (min.x - origin.x) * invD;
75+
double t1 = (max.x - origin.x) * invD;
76+
77+
if (invD < 0.0) swap(t0, t1);
78+
79+
tmin = std::max(t0, tmin);
80+
tmax = std::min(t1, tmax);
81+
82+
if (tmax <= tmin) return false;
83+
84+
// Now test against y-direction.
85+
invD = 1.0 / ray->direction.y;
86+
t0 = (min.y - origin.y) * invD;
87+
t1 = (max.y - origin.y) * invD;
88+
89+
if (invD < 0.0) swap(t0, t1);
90+
91+
tmin = std::max(t0, tmin);
92+
tmax = std::min(t1, tmax);
93+
94+
if (tmax <= tmin) return false;
95+
96+
// Now test against z-direction:
97+
invD = 1.0 / ray->direction.z;
98+
t0 = (min.z - origin.z) * invD;
99+
t1 = (max.z - origin.z) * invD;
100+
101+
if (invD < 0.0) swap(t0, t1);
102+
103+
tmin = std::max(t0, tmin);
104+
tmax = std::min(t1, tmax);
105+
106+
if (tmax <= tmin) return false;
107+
108+
return true;
109+
}

lib/engine/AABB.hpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @file AABB.hpp
3+
* @author Edward Palmer
4+
* @date 2025-03-03
5+
*
6+
* @copyright Copyright (c) 2025
7+
*
8+
*/
9+
10+
#pragma once
11+
12+
extern "C"
13+
{
14+
#include "engine/Ray.h"
15+
#include "utility/Vector3.h"
16+
}
17+
18+
/**
19+
* Axis-aligned bounding box.
20+
*/
21+
class AABB
22+
{
23+
public:
24+
AABB();
25+
AABB(Point3 min, Point3 max);
26+
27+
/** Resets bounding box. */
28+
void reset();
29+
30+
/** Adds new point to bounding box. Recalculates size. */
31+
void addPoint(Point3 pt);
32+
33+
/* Add bounding boxes */
34+
AABB operator+(const AABB &other);
35+
36+
constexpr Point3 &minPt()
37+
{
38+
return min;
39+
}
40+
41+
constexpr Point3 &maxPt()
42+
{
43+
return max;
44+
}
45+
46+
constexpr const Point3 &minPt() const
47+
{
48+
return min;
49+
}
50+
51+
constexpr const Point3 &maxPt() const
52+
{
53+
return max;
54+
}
55+
56+
/** Returns true if box is hit by ray in range [tmin, tmax]. */
57+
bool hit(Ray *ray, double tmin, double tmax);
58+
59+
protected:
60+
/** Adds two bounding boxes and returns the result. */
61+
static AABB addBoundingBoxes(const AABB &box0, const AABB &box1);
62+
63+
Point3 min, max;
64+
};

lib/engine/Material.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ static inline bool refractRay(Ray *incidentRay, HitRec *hit, Ray *scatteredRay,
198198
// If it's the front face, we're going into the object otherwise we're leaving
199199
// and exiting into the air.
200200
const double ir = hit->material->indexOfRefraction;
201-
const double refractionRatio = hit->frontFace ? (1.0 / ir) : ir;
201+
const double refractionRatio =
202+
hit->frontFace ? (1.0 / ir) : ir; // TODO: - we can calculate based on incident ray and normal whether this was
203+
// inside or outside object. Don't need this.
202204

203205
// Total internal reflection:
204206
bool cannotRefract = (refractionRatio * sinTheta > 1.0);

0 commit comments

Comments
 (0)