diff --git a/source/pattern/texturemap.h b/source/pattern/texturemap.h index ba70855..91984ab 100644 --- a/source/pattern/texturemap.h +++ b/source/pattern/texturemap.h @@ -19,6 +19,7 @@ enum TextureMapType SPHERICAL_MAP, PLANAR_MAP, CYLINDRICAL_MAP, + CUBIC_MAP }; class TextureMap : public Pattern @@ -26,6 +27,7 @@ class TextureMap : public Pattern private: TextureMapType type; UVPattern *pattern; + UVPattern *frontPat, *leftPat, *rightPat, *backPat, *upPat, *downPat; public: TextureMap(TextureMapType type, UVPattern *pattern) : Pattern(Colour(0, 0, 0), Colour(0, 0, 0)), type(type), pattern(pattern) { }; @@ -80,25 +82,112 @@ class TextureMap : public Pattern v = modulo(point.y, 1.0); } + enum CubeFaces { + CUBE_LEFT, + CUBE_RIGHT, + CUBE_FRONT, + CUBE_BACK, + CUBE_UP, + CUBE_DOWN, + }; + + static CubeFaces faceFromPoint(Tuple point) { + double abs_x = fabs(point.x); + double abs_y = fabs(point.y); + double abs_z = fabs(point.z); + + double coord = max3(abs_x, abs_y, abs_z); + + if (coord == point.x) { return CUBE_RIGHT; } + if (coord == -point.x) { return CUBE_LEFT; } + if (coord == point.y) { return CUBE_UP; } + if (coord == -point.y) { return CUBE_DOWN; } + if (coord == point.z) { return CUBE_FRONT; } + + return CUBE_BACK; + } + + static void cubeUBFront(Tuple point, double &u, double &v) { + u = modulo(point.x + 1, 2.0) / 2.0; + v = modulo(point.y + 1, 2.0) / 2.0; + } + + static void cubeUBBack(Tuple point, double &u, double &v) { + u = modulo(1 - point.x, 2.0) / 2.0; + v = modulo(point.y + 1, 2.0) / 2.0; + } + + static void cubeUBLeft(Tuple point, double &u, double &v) { + u = modulo(point.z + 1, 2.0) / 2.0; + v = modulo(point.y + 1, 2.0) / 2.0; + } + + static void cubeUBRight(Tuple point, double &u, double &v) { + u = modulo(1 - point.z, 2.0) / 2.0; + v = modulo(point.y + 1, 2.0) / 2.0; + } + + static void cubeUBUp(Tuple point, double &u, double &v) { + u = modulo(point.x + 1, 2.0) / 2.0; + v = modulo(1 - point.z, 2.0) / 2.0; + } + + static void cubeUBDown(Tuple point, double &u, double &v) { + u = modulo(point.x + 1, 2.0) / 2.0; + v = modulo(point.z + 1, 2.0) / 2.0; + } + + void setCubePattern(UVPattern *front, UVPattern *left, UVPattern *right, + UVPattern *back, UVPattern *up, UVPattern *down) + { + this->frontPat = front; + this->leftPat = left; + this->rightPat = right; + this->backPat = back; + this->upPat = up; + this->downPat = down; + } + Colour patternAt(Tuple point) { double u,v; - switch(this->type) + if (this->type == CUBIC_MAP) { - default: - case SPHERICAL_MAP: - this->sphericalMap(point, u, v); - break; - case PLANAR_MAP: - this->planarMap(point, u, v); - break; - - case CYLINDRICAL_MAP: - this->cylindricalMap(point, u, v); - break; + CubeFaces face = this->faceFromPoint(point); + UVPattern *facePat; + double u, v; + switch(face) + { + default: + case CUBE_LEFT: facePat = this->leftPat; this->cubeUBLeft(point, u, v); break; + case CUBE_RIGHT: facePat = this->rightPat; this->cubeUBRight(point, u, v); break; + case CUBE_FRONT: facePat = this->frontPat; this->cubeUBFront(point, u, v); break; + case CUBE_BACK: facePat = this->backPat; this->cubeUBBack(point, u, v); break; + case CUBE_UP: facePat = this->upPat; this->cubeUBUp(point, u, v); break; + case CUBE_DOWN: facePat = this->downPat; this->cubeUBDown(point, u, v); break; + } + + return facePat->uvPatternAt(u, v); + } + else + { + switch (this->type) + { + default: + case SPHERICAL_MAP: + this->sphericalMap(point, u, v); + break; + case PLANAR_MAP: + this->planarMap(point, u, v); + break; + + case CYLINDRICAL_MAP: + this->cylindricalMap(point, u, v); + break; + } + + return this->pattern->uvPatternAt(u, v); } - - return this->pattern->uvPatternAt(u, v); } void dumpMe(FILE *fp) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9345318..57775cd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -83,6 +83,9 @@ target_sources(uvmap_checkeredplane PRIVATE uvmap_checkeredplane.cpp) add_executable(uvmap_checkeredcylinder) target_sources(uvmap_checkeredcylinder PRIVATE uvmap_checkeredcylinder.cpp) +add_executable(uvmap_checkeredcube) +target_sources(uvmap_checkeredcube PRIVATE uvmap_checkeredcube.cpp) + add_executable(uvmap_aligncheckplane) target_sources(uvmap_aligncheckplane PRIVATE uvmap_aligncheckplane.cpp) @@ -103,6 +106,7 @@ add_test(NAME UVMap_CheckeredSphere COMMAND $ add_test(NAME UVMap_CheckeredPlane COMMAND $) add_test(NAME UVMap_CheckeredCylinder COMMAND $) add_test(NAME UVMap_AlignCheckPlane COMMAND $) +add_test(NAME UVMap_CheckeredCube COMMAND $) add_test(NAME Test_Rendering COMMAND $) add_test(NAME Triangle_RenderTest COMMAND $) add_test(NAME ChristmasBall_Rendering COMMAND $) diff --git a/tests/pattern_test.cpp b/tests/pattern_test.cpp index 82ba139..c3694a8 100644 --- a/tests/pattern_test.cpp +++ b/tests/pattern_test.cpp @@ -466,4 +466,242 @@ TEST(PatternTest, Layout_of_the_align_check_pattern) Colour ret = uvac.uvPatternAt(testList[i][0], testList[i][1]); ASSERT_EQ(ret, testResults[i]); } +} + +TEST(PatternTest, Identifying_the_face_of_a_cube_from_a_point) +{ + Point testList[] = { + Point(-1, 0.5, -0.25), + Point(1.1, -0.75, 0.8), + Point(0.1, 0.6, 0.9), + Point(-0.7, 0, -2), + Point(0.5, 1, 0.9), + Point(-0.2, -1.3, 1.1), + }; + + TextureMap::CubeFaces testResults[] { + TextureMap::CUBE_LEFT, + TextureMap::CUBE_RIGHT, + TextureMap::CUBE_FRONT, + TextureMap::CUBE_BACK, + TextureMap::CUBE_UP, + TextureMap::CUBE_DOWN, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + TextureMap::CubeFaces face = tm.faceFromPoint(testList[i]); + ASSERT_EQ(face, testResults[i]); + } +} + +TEST(PatternTest, UV_mapping_the_front_face_of_a_cube) +{ + Point testList[] = { + Point(-0.5, 0.5, 1), + Point(0.5, -0.5, 1), + }; + + double testResults[][2] { + {0.25, 0.75}, + {0.75, 0.25}, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + double u, v; + tm.cubeUBFront(testList[i], u, v); + ASSERT_TRUE(double_equal(u, testResults[i][0])); + ASSERT_TRUE(double_equal(v, testResults[i][1])); + } +} + +TEST(PatternTest, UV_mapping_the_back_face_of_a_cube) +{ + Point testList[] = { + Point(0.5, 0.5, -1), + Point(-0.5, -0.5, -1), + }; + + double testResults[][2] { + {0.25, 0.75}, + {0.75, 0.25}, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + double u, v; + tm.cubeUBBack(testList[i], u, v); + ASSERT_TRUE(double_equal(u, testResults[i][0])); + ASSERT_TRUE(double_equal(v, testResults[i][1])); + } +} + +TEST(PatternTest, UV_mapping_the_left_face_of_a_cube) +{ + Point testList[] = { + Point(-1, 0.5, -0.5), + Point(-1, -0.5, 0.5), + }; + + double testResults[][2] { + {0.25, 0.75}, + {0.75, 0.25}, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + double u, v; + tm.cubeUBLeft(testList[i], u, v); + ASSERT_TRUE(double_equal(u, testResults[i][0])); + ASSERT_TRUE(double_equal(v, testResults[i][1])); + } +} + +TEST(PatternTest, UV_mapping_the_right_face_of_a_cube) +{ + Point testList[] = { + Point(1, 0.5, 0.5), + Point(1, -0.5, -0.5), + }; + + double testResults[][2] { + {0.25, 0.75}, + {0.75, 0.25}, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + double u, v; + tm.cubeUBRight(testList[i], u, v); + ASSERT_TRUE(double_equal(u, testResults[i][0])); + ASSERT_TRUE(double_equal(v, testResults[i][1])); + } +} + +TEST(PatternTest, UV_mapping_the_up_face_of_a_cube) +{ + Point testList[] = { + Point(-0.5, 1, -0.5), + Point(0.5, 1, 0.5), + }; + + double testResults[][2] { + {0.25, 0.75}, + {0.75, 0.25}, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + double u, v; + tm.cubeUBUp(testList[i], u, v); + ASSERT_TRUE(double_equal(u, testResults[i][0])); + ASSERT_TRUE(double_equal(v, testResults[i][1])); + } +} + +TEST(PatternTest, UV_mapping_the_down_face_of_a_cube) +{ + Point testList[] = { + Point(-0.5, -1, 0.5), + Point(0.5, -1, -0.5), + }; + + double testResults[][2] { + {0.25, 0.75}, + {0.75, 0.25}, + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + + for(i = 0; i < testCount; i++) + { + double u, v; + tm.cubeUBDown(testList[i], u, v); + ASSERT_TRUE(double_equal(u, testResults[i][0])); + ASSERT_TRUE(double_equal(v, testResults[i][1])); + } +} + +TEST(PatternTest, Finding_the_colours_on_a_mapped_cube) +{ + Colour red = Colour(1, 0, 0); + Colour yellow = Colour(1, 1, 0); + Colour brown = Colour(1, 0.5, 0); + Colour green = Colour(0, 1, 0); + Colour cyan = Colour(0, 1, 1); + Colour blue = Colour(0, 0, 1); + Colour purple = Colour(1, 0, 1); + Colour white = Colour(1, 1, 1); + + UVAlignCheck left = UVAlignCheck(yellow, cyan, red, blue, brown); + UVAlignCheck front = UVAlignCheck(cyan, red, yellow, brown, green); + UVAlignCheck right = UVAlignCheck(red, yellow, purple, green, white); + UVAlignCheck back = UVAlignCheck(green, purple, cyan, white, blue); + UVAlignCheck up = UVAlignCheck(brown, cyan, purple, red, yellow); + UVAlignCheck down = UVAlignCheck(purple, brown, green, blue, white); + + TextureMap tm = TextureMap(CUBIC_MAP, nullptr); + tm.setCubePattern(&front, &left, &right, &back, &up, &down); + + Point testList[] = { + Point(-1, 0, 0), Point(-1, 0.9, -0.9), Point(-1, 0.9, 0.9), Point(-1, -0.9, -0.9), Point(-1, -0.9, 0.9), /* left */ + Point(0, 0, 1), Point(-0.9, 0.9, 1), Point(0.9, 0.9, 1), Point(-0.9, -0.9, 1), Point(0.9, -0.9, 1), /* front */ + Point(1, 0, 0), Point(1, 0.9, 0.9), Point(1, 0.9, -0.9), Point(1, -0.9, 0.9), Point(1, -0.9, -0.9), /* right */ + Point(0, 0, -1), Point(0.9, 0.9, -1), Point(-0.9, 0.9, -1), Point(0.9, -0.9, -1), Point(-0.9, -0.9, -1), /* back */ + Point(0, 1, 0), Point(-0.9, 1, -0.9), Point(0.9, 1, -0.9), Point(-0.9, 1, 0.9), Point(0.9, 1, 0.9), /* up */ + Point(0, -1, 0), Point(-0.9, -1, 0.9), Point(0.9, -1, 0.9), Point(-0.9, -1, -0.9), Point(0.9, -1, -0.9), /* down */ + }; + + Colour testResults[] { + yellow, cyan, red, blue, brown, /* left */ + cyan, red, yellow, brown, green, /* front */ + red, yellow, purple, green, white, /* right */ + green, purple, cyan, white, blue, /* back */ + brown, cyan, purple, red, yellow, /* up */ + purple, brown, green, blue, white, /* down */ + }; + + int testCount = sizeof(testList)/sizeof((testList)[0]); + int i; + + for(i = 0; i < testCount; i++) + { + Colour ret = tm.patternAt(testList[i]); + ASSERT_EQ(ret, testResults[i]); + } + } \ No newline at end of file diff --git a/tests/uvmap_checkeredcube.cpp b/tests/uvmap_checkeredcube.cpp new file mode 100644 index 0000000..c94de21 --- /dev/null +++ b/tests/uvmap_checkeredcube.cpp @@ -0,0 +1,111 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Render test for chapter 10 + * + * Created by Manoƫl Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +Colour red = Colour(1, 0, 0); +Colour yellow = Colour(1, 1, 0); +Colour brown = Colour(1, 0.5, 0); +Colour green = Colour(0, 1, 0); +Colour cyan = Colour(0, 1, 1); +Colour blue = Colour(0, 0, 1); +Colour purple = Colour(1, 0, 1); +Colour white = Colour(1, 1, 1); + +UVAlignCheck left = UVAlignCheck(yellow, cyan, red, blue, brown); +UVAlignCheck front = UVAlignCheck(cyan, red, yellow, brown, green); +UVAlignCheck right = UVAlignCheck(red, yellow, purple, green, white); +UVAlignCheck back = UVAlignCheck(green, purple, cyan, white, blue); +UVAlignCheck up = UVAlignCheck(brown, cyan, purple, red, yellow); +UVAlignCheck down = UVAlignCheck(purple, brown, green, blue, white); + +Shape *MappedCube() +{ + Cube *ret = new Cube(); + TextureMap *tm = new TextureMap(CUBIC_MAP, nullptr); + tm->setCubePattern(&front, &left, &right, &back, &up, &down); + ret->material.pattern = tm; + ret->material.ambient = 0.2; + ret->material.specular = 0; + ret->material.diffuse = 0.8; + + return ret; +} + +int main() +{ + World w = World(); + + Light light1 = Light(POINT_LIGHT, Point(0, 100, -100), Colour(0.25, 0.25, 0.25)); + w.addLight(&light1); + Light light2 = Light(POINT_LIGHT, Point(0, -100, -100), Colour(0.25, 0.25, 0.25)); + w.addLight(&light2); + Light light3 = Light(POINT_LIGHT, Point(-100, 0, -100), Colour(0.25, 0.25, 0.25)); + w.addLight(&light3); + Light light4 = Light(POINT_LIGHT, Point(100, 0, -100), Colour(0.25, 0.25, 0.25)); + w.addLight(&light4); + + Shape *s; + + s = MappedCube(); + s->setTransform(translation(-6, 2, 0) * rotationX(0.7854) * rotationY(0.7854)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(-2, 2, 0) * rotationX(0.7854) * rotationY(2.3562)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(2, 2, 0) * rotationX(0.7854) * rotationY(3.927)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(6, 2, 0) * rotationX(0.7854) * rotationY(5.4978)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(-6, -2, 0) * rotationX(-0.7854) * rotationY(0.7854)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(-2, -2, 0) * rotationX(-0.7854) * rotationY(2.3562)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(2, -2, 0) * rotationX(-0.7854) * rotationY(3.927)); + w.addObject(s); + + s = MappedCube(); + s->setTransform(translation(6, -2, 0) * rotationX(-0.7854) * rotationY(5.4978)); + w.addObject(s); + + /* Set the camera */ + Camera camera = Camera(800, 400, 0.8); + camera.setTransform(viewTransform(Point(0, 0, -20), + Point(0, 0, 0), + Vector(0, 1, 0))); + + /* Now render it */ + Canvas image = camera.render(w); + + image.SaveAsPNG("uvmap_checkeredcube.png"); + + return 0; +} \ No newline at end of file