Index: src/OFMatrix4x4.h ================================================================== --- src/OFMatrix4x4.h +++ src/OFMatrix4x4.h @@ -21,44 +21,44 @@ * @brief A 4x4 matrix of floats. */ OF_SUBCLASSING_RESTRICTED @interface OFMatrix4x4: OFObject { - float _values[16]; + float _values[4][4]; } #ifdef OF_HAVE_CLASS_PROPERTIES @property (readonly, class) OFMatrix4x4 *identityMatrix; #endif /** - * @brief An array of the 16 floats of the 4x4 matrix in column-major format. + * @brief A 2D array of the 4x4 floats of the matrix in row-major format. * * These may be modified directly. */ -@property (readonly, nonatomic) float *values; +@property (readonly, nonatomic) float (*values)[4][4]; /** * @brief Returns the 4x4 identity matrix. */ + (OFMatrix4x4 *)identityMatrix; /** * @brief Creates a new 4x4 matrix with the specified values. * - * @param values An array of 16 floats in column-major format + * @param values A 2D array of 4x4 floats in row-major format * @return A new, autoreleased OFMatrix4x4 */ -+ (instancetype)matrixWithValues: (const float [_Nonnull 16])values; ++ (instancetype)matrixWithValues: (const float [_Nonnull 4][4])values; /** * @brief Initializes an already allocated 4x4 matrix with the specified values. * - * @param values An array of 16 floats in column-major format + * @param values A 2D array of 4x4 floats in row-major format * @return An initialized OFMatrix4x4 */ -- (instancetype)initWithValues: (const float [_Nonnull 16])values; +- (instancetype)initWithValues: (const float [_Nonnull 4][4])values; /** * @brief Mulitplies the receiver with the specified matrix on the left side * and the receiver on the right side. * Index: src/OFMatrix4x4.m ================================================================== --- src/OFMatrix4x4.m +++ src/OFMatrix4x4.m @@ -17,41 +17,41 @@ #import "OFMatrix4x4.h" #import "OFOnce.h" #import "OFString.h" -static const float identityValues[16] = { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 +static const float identityValues[4][4] = { + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 } }; @implementation OFMatrix4x4 + (OFMatrix4x4 *)identityMatrix { return [[[OFMatrix4x4 alloc] initWithValues: identityValues] autorelease]; } -+ (instancetype)matrixWithValues: (const float [16])values ++ (instancetype)matrixWithValues: (const float [4][4])values { return [[[self alloc] initWithValues: values] autorelease]; } -- (instancetype)initWithValues: (const float [16])values +- (instancetype)initWithValues: (const float [4][4])values { self = [super init]; - memcpy(_values, values, 16 * sizeof(float)); + memcpy(_values, values, sizeof(_values)); return self; } -- (float *)values +- (float (*)[4][4])values { - return _values; + return &_values; } - (instancetype)copy { return [[OFMatrix4x4 alloc] initWithValues: _values]; @@ -60,85 +60,109 @@ - (bool)isEqual: (OFMatrix4x4 *)matrix { if (![matrix isKindOfClass: [OFMatrix4x4 class]]) return false; - return (memcmp(_values, matrix->_values, 16 * sizeof(float)) == 0); + return (memcmp(_values, matrix->_values, sizeof(_values)) == 0); } - (unsigned long)hash { unsigned long hash; OFHashInit(&hash); - for (size_t i = 0; i < 16; i++) - OFHashAddHash(&hash, OFFloatToRawUInt32(_values[i])); + for (uint_fast8_t i = 0; i < 4; i++) + for (uint_fast8_t j = 0; j < 4; j++) + OFHashAddHash(&hash, OFFloatToRawUInt32(_values[i][j])); OFHashFinalize(&hash); return hash; } - (void)multiplyWithMatrix: (OFMatrix4x4 *)matrix { - float r[16], *m = _values, *l = matrix->_values; - memcpy(r, m, sizeof(r)); - - m[ 0] = l[0] * r[ 0] + l[4] * r[ 1] + l[ 8] * r[ 2] + l[12] * r[ 3]; - m[ 1] = l[1] * r[ 0] + l[5] * r[ 1] + l[ 9] * r[ 2] + l[13] * r[ 3]; - m[ 2] = l[2] * r[ 0] + l[6] * r[ 1] + l[10] * r[ 2] + l[14] * r[ 3]; - m[ 3] = l[3] * r[ 0] + l[7] * r[ 1] + l[11] * r[ 2] + l[15] * r[ 3]; - m[ 4] = l[0] * r[ 4] + l[4] * r[ 5] + l[ 8] * r[ 6] + l[12] * r[ 7]; - m[ 5] = l[1] * r[ 4] + l[5] * r[ 5] + l[ 9] * r[ 6] + l[13] * r[ 7]; - m[ 6] = l[2] * r[ 4] + l[6] * r[ 5] + l[10] * r[ 6] + l[14] * r[ 7]; - m[ 7] = l[3] * r[ 4] + l[7] * r[ 5] + l[11] * r[ 6] + l[15] * r[ 7]; - m[ 8] = l[0] * r[ 8] + l[4] * r[ 9] + l[ 8] * r[10] + l[12] * r[11]; - m[ 9] = l[1] * r[ 8] + l[5] * r[ 9] + l[ 9] * r[10] + l[13] * r[11]; - m[10] = l[2] * r[ 8] + l[6] * r[ 9] + l[10] * r[10] + l[14] * r[11]; - m[11] = l[3] * r[ 8] + l[7] * r[ 9] + l[11] * r[10] + l[15] * r[11]; - m[12] = l[0] * r[12] + l[4] * r[13] + l[ 8] * r[14] + l[12] * r[15]; - m[13] = l[1] * r[12] + l[5] * r[13] + l[ 9] * r[14] + l[13] * r[15]; - m[14] = l[2] * r[12] + l[6] * r[13] + l[10] * r[14] + l[14] * r[15]; - m[15] = l[3] * r[12] + l[7] * r[13] + l[11] * r[14] + l[15] * r[15]; + float right[4][4]; + memcpy(right, _values, sizeof(right)); + +#define left matrix->_values + _values[0][0] = left[0][0] * right[0][0] + left[0][1] * right[1][0] + + left[0][2] * right[2][0] + left[0][3] * right[3][0]; + _values[0][1] = left[0][0] * right[0][1] + left[0][1] * right[1][1] + + left[0][2] * right[2][1] + left[0][3] * right[3][1]; + _values[0][2] = left[0][0] * right[0][2] + left[0][1] * right[1][2] + + left[0][2] * right[2][2] + left[0][3] * right[3][2]; + _values[0][3] = left[0][0] * right[0][3] + left[0][1] * right[1][3] + + left[0][2] * right[2][3] + left[0][3] * right[3][3]; + + _values[1][0] = left[1][0] * right[0][0] + left[1][1] * right[1][0] + + left[1][2] * right[2][0] + left[1][3] * right[3][0]; + _values[1][1] = left[1][0] * right[0][1] + left[1][1] * right[1][1] + + left[1][2] * right[2][1] + left[1][3] * right[3][1]; + _values[1][2] = left[1][0] * right[0][2] + left[1][1] * right[1][2] + + left[1][2] * right[2][2] + left[1][3] * right[3][2]; + _values[1][3] = left[1][0] * right[0][3] + left[1][1] * right[1][3] + + left[1][2] * right[2][3] + left[1][3] * right[3][3]; + + _values[2][0] = left[2][0] * right[0][0] + left[2][1] * right[1][0] + + left[2][2] * right[2][0] + left[2][3] * right[3][0]; + _values[2][1] = left[2][0] * right[0][1] + left[2][1] * right[1][1] + + left[2][2] * right[2][1] + left[2][3] * right[3][1]; + _values[2][2] = left[2][0] * right[0][2] + left[2][1] * right[1][2] + + left[2][2] * right[2][2] + left[2][3] * right[3][2]; + _values[2][3] = left[2][0] * right[0][3] + left[2][1] * right[1][3] + + left[2][2] * right[2][3] + left[2][3] * right[3][3]; + + _values[3][0] = left[3][0] * right[0][0] + left[3][1] * right[1][0] + + left[3][2] * right[2][0] + left[3][3] * right[3][0]; + _values[3][1] = left[3][0] * right[0][1] + left[3][1] * right[1][1] + + left[3][2] * right[2][1] + left[3][3] * right[3][1]; + _values[3][2] = left[3][0] * right[0][2] + left[3][1] * right[1][2] + + left[3][2] * right[2][2] + left[3][3] * right[3][2]; + _values[3][3] = left[3][0] * right[0][3] + left[3][1] * right[1][3] + + left[3][2] * right[2][3] + left[3][3] * right[3][3]; +#undef left } - (void)translateWithVector: (OFVector3D)vector { OFMatrix4x4 *translation = [[OFMatrix4x4 alloc] initWithValues: - (float [16]){ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - vector.x, vector.y, vector.z, 1 + (float [4][4]){ + { 1, 0, 0, vector.x }, + { 0, 1, 0, vector.y }, + { 0, 0, 1, vector.z }, + { 0, 0, 0, 1 } }]; [self multiplyWithMatrix: translation]; [translation release]; } - (void)scaleWithVector: (OFVector3D)vector { OFMatrix4x4 *scale = [[OFMatrix4x4 alloc] initWithValues: - (float [16]){ - vector.x, 0, 0, 0, - 0, vector.y, 0, 0, - 0, 0, vector.z, 0, - 0, 0, 0, 1 + (float [4][4]){ + { vector.x, 0, 0, 0 }, + { 0, vector.y, 0, 0 }, + { 0, 0, vector.z, 0 }, + { 0, 0, 0, 1 } }]; [self multiplyWithMatrix: scale]; [scale release]; } -- (OFVector4D)transformedVector: (OFVector4D)vec +- (OFVector4D)transformedVector: (OFVector4D)vector { - float *m = _values; - return OFMakeVector4D( - m[0] * vec.x + m[4] * vec.y + m[ 8] * vec.z + m[12] * vec.w, - m[1] * vec.x + m[5] * vec.y + m[ 9] * vec.z + m[13] * vec.w, - m[2] * vec.x + m[6] * vec.y + m[10] * vec.z + m[14] * vec.w, - m[3] * vec.x + m[7] * vec.y + m[11] * vec.z + m[15] * vec.w); + _values[0][0] * vector.x + _values[0][1] * vector.y + + _values[0][2] * vector.z + _values[0][3] * vector.w, + _values[1][0] * vector.x + _values[1][1] * vector.y + + _values[1][2] * vector.z + _values[1][3] * vector.w, + _values[2][0] * vector.x + _values[2][1] * vector.y + + _values[2][2] * vector.z + _values[2][3] * vector.w, + _values[3][0] * vector.x + _values[3][1] * vector.y + + _values[3][2] * vector.z + _values[3][3] * vector.w); } - (OFString *)description { return [OFString stringWithFormat: @@ -146,11 +170,11 @@ @"\t%g %g %g %g\n" @"\t%g %g %g %g\n" @"\t%g %g %g %g\n" @"\t%g %g %g %g\n" @"}>", - _values[0], _values[4], _values[8], _values[12], - _values[1], _values[5], _values[9], _values[13], - _values[2], _values[6], _values[10], _values[14], - _values[3], _values[7], _values[11], _values[15]]; + _values[0][0], _values[0][1], _values[0][2], _values[0][3], + _values[1][0], _values[1][1], _values[1][2], _values[1][3], + _values[2][0], _values[2][1], _values[2][2], _values[2][3], + _values[3][0], _values[3][1], _values[3][2], _values[3][3]]; } @end Index: tests/OFMatrix4x4Tests.m ================================================================== --- tests/OFMatrix4x4Tests.m +++ tests/OFMatrix4x4Tests.m @@ -25,23 +25,23 @@ void *pool = objc_autoreleasePoolPush(); OFMatrix4x4 *matrix, *matrix2; OFVector4D point; TEST(@"+[identityMatrix]", - memcmp([[OFMatrix4x4 identityMatrix] values], (float [16]){ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + memcmp([[OFMatrix4x4 identityMatrix] values], (float [4][4]){ + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 } }, 16 * sizeof(float)) == 0) TEST(@"+[matrixWithValues:]", - (matrix = [OFMatrix4x4 matrixWithValues: (float [16]){ - 1, 5, 9, 13, - 2, 6, 10, 14, - 3, 7, 11, 15, - 4, 8, 12, 16 + (matrix = [OFMatrix4x4 matrixWithValues: (float [4][4]){ + { 1, 2, 3, 4 }, + { 5, 6, 7, 8 }, + { 9, 10, 11, 12 }, + { 13, 14, 15, 16 } }])) TEST(@"-[description]", [matrix.description isEqual: @""]) TEST(@"-[isEqual:]", [[OFMatrix4x4 identityMatrix] isEqual: - [OFMatrix4x4 matrixWithValues: (float [16]){ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + [OFMatrix4x4 matrixWithValues: (float [4][4]){ + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 } }]]) TEST(@"-[copy]", (matrix2 = [matrix copy]) && [matrix2 isEqual: matrix]) TEST(@"-[multiplyWithMatrix:] #1", R([matrix2 multiplyWithMatrix: [OFMatrix4x4 identityMatrix]]) && [matrix2 isEqual: matrix]) - matrix2 = [OFMatrix4x4 matrixWithValues: (float [16]){ - 100, 500, 900, 1300, - 200, 600, 1000, 1400, - 300, 700, 1100, 1500, - 400, 800, 1200, 1600 + matrix2 = [OFMatrix4x4 matrixWithValues: (float [4][4]){ + { 100, 200, 300, 400 }, + { 500, 600, 700, 800 }, + { 900, 1000, 1100, 1200 }, + { 1300, 1400, 1500, 1600 } }]; TEST(@"-[multiplyWithMatrix:] #2", R([matrix2 multiplyWithMatrix: matrix]) && - [matrix2 isEqual: [OFMatrix4x4 matrixWithValues: (float [16]){ - 9000, 20200, 31400, 42600, - 10000, 22800, 35600, 48400, - 11000, 25400, 39800, 54200, - 12000, 28000, 44000, 60000 + [matrix2 isEqual: [OFMatrix4x4 matrixWithValues: (float [4][4]){ + { 9000, 10000, 11000, 12000 }, + { 20200, 22800, 25400, 28000 }, + { 31400, 35600, 39800, 44000 }, + { 42600, 48400, 54200, 60000 } }]]) TEST(@"[-translateWithVector:]", R(matrix2 = [OFMatrix4x4 identityMatrix]) && R([matrix2 translateWithVector: OFMakeVector3D(1, 2, 3)]) &&