chapter 4
This commit is contained in:
parent
a380038f5e
commit
39e0d00e52
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
test/test_tuples
|
test/test_tuples
|
||||||
test/test_canvas
|
test/test_canvas
|
||||||
test/test_matrices
|
test/test_matrices
|
||||||
|
test/test_transformations
|
||||||
*.ppm
|
*.ppm
|
4
float.h
4
float.h
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
inline static bool float_equal(float a, float b)
|
inline static bool float_equal(float a, float b)
|
||||||
{
|
{
|
||||||
const float epsilon = 0.00001;
|
const float epsilon = 0.00001;
|
||||||
return __builtin_fabs(a - b) < epsilon;
|
return fabsf(a - b) < epsilon;
|
||||||
}
|
}
|
||||||
|
6
math.h
Normal file
6
math.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define sqrtf __builtin_sqrtf
|
||||||
|
#define fabsf __builtin_fabsf
|
||||||
|
#define cosf __builtin_cosf
|
||||||
|
#define sinf __builtin_sinf
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -eux
|
set -eux
|
||||||
|
|
||||||
for name in tuples canvas matrices; do
|
for name in tuples canvas matrices transformations; do
|
||||||
gcc -g -gdwarf-5 \
|
gcc -g -gdwarf-5 \
|
||||||
-Wall -Werror -Wfatal-errors \
|
-Wall -Werror -Wfatal-errors \
|
||||||
-I. \
|
-I. \
|
||||||
|
234
test/test_transformations.c
Normal file
234
test/test_transformations.c
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "transformations.h"
|
||||||
|
#include "runner.h"
|
||||||
|
|
||||||
|
static bool transformations_test_0(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Multiplying by a translation matrix";
|
||||||
|
|
||||||
|
struct mat4x4 transform = translation(5.0f, -3.0f, 2.0f);
|
||||||
|
struct tuple p = point(-3.0f, 4.0f, 5.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 1.0f, 7.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_1(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Multiplying by the inverse of a translation matrix";
|
||||||
|
|
||||||
|
struct mat4x4 transform = translation(5.0f, -3.0f, 2.0f);
|
||||||
|
struct mat4x4 inv = mat4x4_inverse(&transform);
|
||||||
|
struct tuple p = point(-3.0f, 4.0f, 5.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&inv, &p), point(-8.0f, 7.0f, 3.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_2(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Translation does not affect vectors";
|
||||||
|
|
||||||
|
struct mat4x4 transform = translation(5.0f, -3.0f, 2.0f);
|
||||||
|
struct tuple v = vector(-3.0f, 4.0f, 5.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &v), v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_3(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A scaling matrix applied to a point";
|
||||||
|
|
||||||
|
struct mat4x4 transform = scaling(2.0f, 3.0f, 4.0f);
|
||||||
|
struct tuple p = point(-4.0f, 6.0f, 8.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(-8.0f, 18.0f, 32.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_4(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A scaling matrix applied to a vector";
|
||||||
|
|
||||||
|
struct mat4x4 transform = scaling(2.0f, 3.0f, 4.0f);
|
||||||
|
struct tuple v = vector(-4.0f, 6.0f, 8.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &v), vector(-8.0f, 18.0f, 32.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_5(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Multiplying by the inverse of a scaling matrix";
|
||||||
|
|
||||||
|
struct mat4x4 transform = scaling(2.0f, 3.0f, 4.0f);
|
||||||
|
struct mat4x4 inv = mat4x4_inverse(&transform);
|
||||||
|
struct tuple v = vector(-4.0f, 6.0f, 8.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&inv, &v), vector(-2.0f, 2.0f, 2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_6(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Reflection is scaling by a negative value";
|
||||||
|
|
||||||
|
struct mat4x4 transform = scaling(-1.0f, 1.0f, 1.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(-2.0f, 3.0f, 4.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_7(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Rotating a point around the x axis";
|
||||||
|
|
||||||
|
struct tuple p = point(0.0f, 1.0f, 0.0f);
|
||||||
|
struct mat4x4 half_quarter = rotation_x(tau / 8);
|
||||||
|
struct mat4x4 full_quarter = rotation_x(tau / 4);
|
||||||
|
|
||||||
|
return
|
||||||
|
tuple_equal(mat4x4_mul_t(&half_quarter, &p), point(0.0f, 0.7071067811865476, 0.7071067811865476)) &&
|
||||||
|
tuple_equal(mat4x4_mul_t(&full_quarter, &p), point(0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_8(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Rotating a point around the y axis";
|
||||||
|
|
||||||
|
struct tuple p = point(0.0f, 0.0f, 1.0f);
|
||||||
|
struct mat4x4 half_quarter = rotation_y(tau / 8);
|
||||||
|
struct mat4x4 full_quarter = rotation_y(tau / 4);
|
||||||
|
|
||||||
|
return
|
||||||
|
tuple_equal(mat4x4_mul_t(&half_quarter, &p), point(0.7071067811865476, 0.0f, 0.7071067811865476)) &&
|
||||||
|
tuple_equal(mat4x4_mul_t(&full_quarter, &p), point(1.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_9(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Rotating a point around the z axis";
|
||||||
|
|
||||||
|
struct tuple p = point(0.0f, 1.0f, 0.0f);
|
||||||
|
struct mat4x4 half_quarter = rotation_z(tau / 8);
|
||||||
|
struct mat4x4 full_quarter = rotation_z(tau / 4);
|
||||||
|
|
||||||
|
return
|
||||||
|
tuple_equal(mat4x4_mul_t(&half_quarter, &p), point(-0.7071067811865476, 0.7071067811865476, 0.0f)) &&
|
||||||
|
tuple_equal(mat4x4_mul_t(&full_quarter, &p), point(-1.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_10(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A shearing transformation moves x in proportion to y";
|
||||||
|
|
||||||
|
struct mat4x4 transform = shearing(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(5.0f, 3.0f, 4.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_11(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A shearing transformation moves x in proportion to z";
|
||||||
|
|
||||||
|
struct mat4x4 transform = shearing(0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(6.0f, 3.0f, 4.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_12(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A shearing transformation moves y in proportion to x";
|
||||||
|
|
||||||
|
struct mat4x4 transform = shearing(0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 5.0f, 4.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_13(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A shearing transformation moves y in proportion to z";
|
||||||
|
|
||||||
|
struct mat4x4 transform = shearing(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 7.0f, 4.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_14(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A shearing transformation moves z in proportion to x";
|
||||||
|
|
||||||
|
struct mat4x4 transform = shearing(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 3.0f, 6.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_15(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "A shearing transformation moves z in proportion to y";
|
||||||
|
|
||||||
|
struct mat4x4 transform = shearing(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
struct tuple p = point(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 3.0f, 7.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_16(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Individual transforms are applied in sequence";
|
||||||
|
|
||||||
|
struct tuple p = point(1.0f, 0.0f, 1.0f);
|
||||||
|
struct mat4x4 a = rotation_x(tau / 4.0f);
|
||||||
|
struct mat4x4 b = scaling(5.0f, 5.0f, 5.0f);
|
||||||
|
struct mat4x4 c = translation(10.0f, 5.0f, 7.0f);
|
||||||
|
|
||||||
|
struct tuple p2 = mat4x4_mul_t(&a, &p);
|
||||||
|
struct tuple p3 = mat4x4_mul_t(&b, &p2);
|
||||||
|
struct tuple p4 = mat4x4_mul_t(&c, &p3);
|
||||||
|
|
||||||
|
return
|
||||||
|
tuple_equal(p2, point(1.0f, -1.0f, 0.0f)) &&
|
||||||
|
tuple_equal(p3, point(5.0f, -5.0f, 0.0f)) &&
|
||||||
|
tuple_equal(p4, point(15.0f, 0.0f, 7.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool transformations_test_17(const char ** scenario)
|
||||||
|
{
|
||||||
|
*scenario = "Chained transformations must be applied in reverse order";
|
||||||
|
|
||||||
|
struct tuple p = point(1.0f, 0.0f, 1.0f);
|
||||||
|
struct mat4x4 a = rotation_x(tau / 4.0f);
|
||||||
|
struct mat4x4 b = scaling(5.0f, 5.0f, 5.0f);
|
||||||
|
struct mat4x4 c = translation(10.0f, 5.0f, 7.0f);
|
||||||
|
|
||||||
|
struct mat4x4 t1 = mat4x4_mul_m(&c, &b);
|
||||||
|
struct mat4x4 t2 = mat4x4_mul_m(&t1, &a);
|
||||||
|
struct tuple p1 = mat4x4_mul_t(&t2, &p);
|
||||||
|
|
||||||
|
return tuple_equal(p1, point(15.0f, 0.0f, 7.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
test_t transformations_tests[] = {
|
||||||
|
transformations_test_0,
|
||||||
|
transformations_test_1,
|
||||||
|
transformations_test_2,
|
||||||
|
transformations_test_3,
|
||||||
|
transformations_test_4,
|
||||||
|
transformations_test_5,
|
||||||
|
transformations_test_6,
|
||||||
|
transformations_test_7,
|
||||||
|
transformations_test_8,
|
||||||
|
transformations_test_9,
|
||||||
|
transformations_test_10,
|
||||||
|
transformations_test_11,
|
||||||
|
transformations_test_12,
|
||||||
|
transformations_test_13,
|
||||||
|
transformations_test_14,
|
||||||
|
transformations_test_15,
|
||||||
|
transformations_test_16,
|
||||||
|
transformations_test_17,
|
||||||
|
};
|
||||||
|
|
||||||
|
RUNNER(transformations_tests)
|
59
transformations.h
Normal file
59
transformations.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "math.h"
|
||||||
|
#include "matrices.h"
|
||||||
|
|
||||||
|
inline static struct mat4x4 translation(float x, float y, float z)
|
||||||
|
{
|
||||||
|
return mat4x4(1.0f, 0.0f, 0.0f, x,
|
||||||
|
0.0f, 1.0f, 0.0f, y,
|
||||||
|
0.0f, 0.0f, 1.0f, z,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static struct mat4x4 scaling(float x, float y, float z)
|
||||||
|
{
|
||||||
|
return mat4x4(x, 0.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, y, 0.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, z, 0.0f,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float tau = 6.283185307179586;
|
||||||
|
|
||||||
|
inline static struct mat4x4 rotation_x(float r)
|
||||||
|
{
|
||||||
|
return mat4x4(1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, cosf(r), -sinf(r), 0.0f,
|
||||||
|
0.0f, sinf(r), cosf(r), 0.0f,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static struct mat4x4 rotation_y(float r)
|
||||||
|
{
|
||||||
|
return mat4x4( cosf(r), 0.0f, sinf(r), 0.0f,
|
||||||
|
0.0f, 1.0f, 0.0f, 0.0f,
|
||||||
|
-sinf(r), 0.0f, cosf(r), 0.0f,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static struct mat4x4 rotation_z(float r)
|
||||||
|
{
|
||||||
|
return mat4x4( cosf(r), -sinf(r), 0.0f, 0.0f,
|
||||||
|
sinf(r), cosf(r), 0.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static struct mat4x4 shearing(float xy,
|
||||||
|
float xz,
|
||||||
|
float yx,
|
||||||
|
float yz,
|
||||||
|
float zx,
|
||||||
|
float zy)
|
||||||
|
{
|
||||||
|
return mat4x4(1.0f, xy, xz, 0.0f,
|
||||||
|
yx, 1.0f, yz, 0.0f,
|
||||||
|
zx, zy, 1.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
9
tuples.h
9
tuples.h
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "math.h"
|
||||||
#include "float.h"
|
#include "float.h"
|
||||||
|
|
||||||
struct tuple {
|
struct tuple {
|
||||||
@ -113,10 +114,10 @@ inline static struct tuple tuple_div(struct tuple a, float s)
|
|||||||
|
|
||||||
inline static float tuple_magnitude(struct tuple a)
|
inline static float tuple_magnitude(struct tuple a)
|
||||||
{
|
{
|
||||||
return __builtin_sqrtf(a.x * a.x +
|
return sqrtf(a.x * a.x +
|
||||||
a.y * a.y +
|
a.y * a.y +
|
||||||
a.z * a.z +
|
a.z * a.z +
|
||||||
a.w * a.w);
|
a.w * a.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static struct tuple tuple_normalize(struct tuple a)
|
inline static struct tuple tuple_normalize(struct tuple a)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user