[simd] Add 2d vector types

For int, long, float and double. I've been meaning to add them for a
while, and they're part of the new Ruamoko instructions set (which is
progressing nicely).
This commit is contained in:
Bill Currie 2022-01-02 00:57:55 +09:00
parent 365762b8a6
commit 97034d9dde
9 changed files with 395 additions and 44 deletions

View file

@ -30,7 +30,8 @@
#include <stdint.h>
#define VEC_TYPE(t,n) typedef t n __attribute__ ((vector_size (4*sizeof (t))))
#define VEC_TYPE(t,n,s) \
typedef t n __attribute__ ((vector_size (s*sizeof (t))))
/** Three element vector type for interfacing with compact data.
*
@ -39,6 +40,9 @@
*/
typedef double vec3d_t[3];
VEC_TYPE (double, vec2d_t, 2);
VEC_TYPE (int64_t, vec2l_t, 2);
#ifdef __AVX2__
/** Four element vector type for horizontal (AOS) vector data.
*
@ -49,11 +53,11 @@ typedef double vec3d_t[3];
* a single component from four vectors, or a single row/column (depending on
* context) of an Nx4 or 4xN matrix.
*/
VEC_TYPE (double, vec4d_t);
VEC_TYPE (double, vec4d_t, 4);
/** Used mostly for __builtin_shuffle.
*/
VEC_TYPE (int64_t, vec4l_t);
VEC_TYPE (int64_t, vec4l_t, 4);
#endif
/** Three element vector type for interfacing with compact data.
@ -63,6 +67,9 @@ VEC_TYPE (int64_t, vec4l_t);
*/
typedef float vec3f_t[3];
VEC_TYPE (float, vec2f_t, 2);
VEC_TYPE (int, vec2i_t, 2);
/** Four element vector type for horizontal (AOS) vector data.
*
* This is used for both vectors (3D and 4D) and quaternions. 3D vectors
@ -72,20 +79,25 @@ typedef float vec3f_t[3];
* a single component from four vectors, or a single row/column (depending on
* context) of an Nx4 or 4xN matrix.
*/
VEC_TYPE (float, vec4f_t);
VEC_TYPE (float, vec4f_t, 4);
/** Used mostly for __builtin_shuffle.
*/
VEC_TYPE (int, vec4i_t);
VEC_TYPE (int, vec4i_t, 4);
#define VEC2D_FMT "[%.17g, %.17g]"
#define VEC2L_FMT "[%ld, %ld]"
#define VEC4D_FMT "[%.17g, %.17g, %.17g, %.17g]"
#if __WORDSIZE == 64
#define VEC4L_FMT "[%ld, %ld, %ld, %ld]"
#else
#define VEC4L_FMT "[%lld, %lld, %lld, %lld]"
#endif
#define VEC2F_FMT "[%.9g, %.9g]"
#define VEC2I_FMT "[%d, %d]"
#define VEC4F_FMT "[%.9g, %.9g, %.9g, %.9g]"
#define VEC4I_FMT "[%d, %d, %d, %d]"
#define VEC2_EXP(v) (v)[0], (v)[1]
#define VEC4_EXP(v) (v)[0], (v)[1], (v)[2], (v)[3]
#define MAT4_ROW(m, r) (m)[0][r], (m)[1][r], (m)[2][r], (m)[3][r]

115
include/QF/simd/vec2d.h Normal file
View file

@ -0,0 +1,115 @@
/*
QF/simd/vec2d.h
Vector functions for vec2d_t (ie, double precision)
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef __QF_simd_vec2d_h
#define __QF_simd_vec2d_h
#include <immintrin.h>
#include "QF/simd/types.h"
GNU89INLINE inline vec2d_t vsqrt2d (vec2d_t v) __attribute__((const));
GNU89INLINE inline vec2d_t vceil2d (vec2d_t v) __attribute__((const));
GNU89INLINE inline vec2d_t vfloor2d (vec2d_t v) __attribute__((const));
GNU89INLINE inline vec2d_t vtrunc2d (vec2d_t v) __attribute__((const));
/** 2D vector dot product.
*/
GNU89INLINE inline vec2d_t dot2d (vec2d_t a, vec2d_t b) __attribute__((const));
GNU89INLINE inline vec2d_t cmuld (vec2d_t a, vec2d_t b) __attribute__((const));
#ifndef IMPLEMENT_VEC2D_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2d_t
vsqrt2d (vec2d_t v)
{
return _mm_sqrt_pd (v);
}
#ifndef IMPLEMENT_VEC2D_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2d_t
vceil2d (vec2d_t v)
{
return _mm_ceil_pd (v);
}
#ifndef IMPLEMENT_VEC2D_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2d_t
vfloor2d (vec2d_t v)
{
return _mm_floor_pd (v);
}
#ifndef IMPLEMENT_VEC2D_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2d_t
vtrunc2d (vec2d_t v)
{
return _mm_round_pd (v, _MM_FROUND_TRUNC);
}
#ifndef IMPLEMENT_VEC2D_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2d_t
dot2d (vec2d_t a, vec2d_t b)
{
vec2d_t c = a * b;
c = _mm_hadd_pd (c, c);
return c;
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2d_t
cmuld (vec2d_t a, vec2d_t b)
{
vec2d_t c = a * b[0];
c = _mm_addsub_pd (c, (vec2d_t) { c[1], c[0] });
return c;
}
#endif//__QF_simd_vec2d_h

166
include/QF/simd/vec2f.h Normal file
View file

@ -0,0 +1,166 @@
/*
QF/simd/vec2f.h
Vector functions for vec2f_t (ie, float precision)
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifndef __QF_simd_vec2f_h
#define __QF_simd_vec2f_h
#include <immintrin.h>
#include <math.h>
#include "QF/simd/types.h"
GNU89INLINE inline vec2f_t vabs2f (vec2f_t v) __attribute__((const));
GNU89INLINE inline vec2f_t vsqrt2f (vec2f_t v) __attribute__((const));
GNU89INLINE inline vec2f_t vceil2f (vec2f_t v) __attribute__((const));
GNU89INLINE inline vec2f_t vfloor2f (vec2f_t v) __attribute__((const));
GNU89INLINE inline vec2f_t vtrunc2f (vec2f_t v) __attribute__((const));
/** 2D vector dot product.
*/
GNU89INLINE inline vec2f_t dot2f (vec2f_t a, vec2f_t b) __attribute__((const));
GNU89INLINE inline vec2f_t cmulf (vec2f_t a, vec2f_t b) __attribute__((const));
GNU89INLINE inline vec2f_t normal2f (vec2f_t v) __attribute__((pure));
GNU89INLINE inline vec2f_t magnitude2f (vec2f_t v) __attribute__((pure));
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
vabs2f (vec2f_t v)
{
const uint32_t nan = ~0u >> 1;
const vec2i_t abs = { nan, nan };
return (vec2f_t) ((vec2i_t) v & abs);
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
vsqrt2f (vec2f_t v)
{
vec4f_t t = { v[0], v[1], 0, 0 };
t = _mm_sqrt_ps (t);
return (vec2f_t) { t[0], t[1] };
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
vceil2f (vec2f_t v)
{
vec4f_t t = { v[0], v[1], 0, 0 };
t = _mm_ceil_ps (t);
return (vec2f_t) { t[0], t[1] };
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
vfloor2f (vec2f_t v)
{
vec4f_t t = { v[0], v[1], 0, 0 };
t = _mm_floor_ps (t);
return (vec2f_t) { t[0], t[1] };
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
vtrunc2f (vec2f_t v)
{
vec4f_t t = { v[0], v[1], 0, 0 };
t = _mm_round_ps (t, _MM_FROUND_TRUNC);
return (vec2f_t) { t[0], t[1] };
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
dot2f (vec2f_t a, vec2f_t b)
{
vec2f_t c = a * b;
vec4f_t t = { c[0], c[1], 0, 0 };
t = _mm_hadd_ps (t, t);
return (vec2f_t) { t[0], t[1] };
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
cmulf (vec2f_t a, vec2f_t b)
{
vec2f_t c1 = a * b[0];
vec2f_t c2 = a * b[1];
vec4f_t c14 ={ c1[0], c1[1], 0, 0 };
vec4f_t c24 ={ c2[1], c2[0], 0, 0 };
vec4f_t c = _mm_addsub_ps (c14, c24);
return (vec2f_t) { c[0], c[1] };
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
normal2f (vec2f_t v)
{
return v / vsqrt2f (dot2f (v, v));
}
#ifndef IMPLEMENT_VEC2F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec2f_t
magnitude2f (vec2f_t v)
{
return vsqrt2f (dot2f (v, v));
}
#endif//__QF_simd_vec2f_h

View file

@ -33,10 +33,10 @@
#include "QF/simd/types.h"
GNU89INLINE inline vec4d_t vsqrtd (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vceild (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vfloord (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vtruncd (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vsqrt4d (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vceil4d (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vfloor4d (vec4d_t v) __attribute__((const));
GNU89INLINE inline vec4d_t vtrunc4d (vec4d_t v) __attribute__((const));
/** 3D vector cross product.
*
* The w (4th) component can be any value on input, and is guaranteed to be 0
@ -96,6 +96,8 @@ GNU89INLINE inline vec4d_t qrotd (vec4d_t a, vec4d_t b) __attribute__((const));
GNU89INLINE inline vec4d_t qconjd (vec4d_t q) __attribute__((const));
GNU89INLINE inline vec4d_t loadvec3d (const double v3[]) __attribute__((pure));
GNU89INLINE inline void storevec3d (double v3[3], vec4d_t v4);
GNU89INLINE inline vec4l_t loadvec3l (const long *v3) __attribute__((pure));
GNU89INLINE inline void storevec3l (long *v3, vec4l_t v4);
#ifndef IMPLEMENT_VEC4D_Funcs
GNU89INLINE inline
@ -103,7 +105,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4d_t
vsqrtd (vec4d_t v)
vsqrt4d (vec4d_t v)
{
return _mm256_sqrt_pd (v);
}
@ -114,7 +116,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4d_t
vceild (vec4d_t v)
vceil4d (vec4d_t v)
{
return _mm256_ceil_pd (v);
}
@ -125,7 +127,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4d_t
vfloord (vec4d_t v)
vfloor4d (vec4d_t v)
{
return _mm256_floor_pd (v);
}
@ -136,7 +138,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4d_t
vtruncd (vec4d_t v)
vtrunc4d (vec4d_t v)
{
return _mm256_round_pd (v, _MM_FROUND_TRUNC);
}
@ -241,11 +243,11 @@ VISIBLE
vec4d_t
qrotd (vec4d_t a, vec4d_t b)
{
vec4d_t ma = vsqrtd (dotd (a, a));
vec4d_t mb = vsqrtd (dotd (b, b));
vec4d_t ma = vsqrt4d (dotd (a, a));
vec4d_t mb = vsqrt4d (dotd (b, b));
vec4d_t den = 2 * ma * mb;
vec4d_t t = mb * a + ma * b;
vec4d_t mba_mab = vsqrtd (dotd (t, t));
vec4d_t mba_mab = vsqrt4d (dotd (t, t));
vec4d_t q = crossd (a, b) / mba_mab;
q[3] = (mba_mab / den)[0];
return q;
@ -293,6 +295,31 @@ storevec3d (double v3[3], vec4d_t v4)
v3[2] = v4[2];
}
#ifndef IMPLEMENT_VEC4F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec4l_t
loadvec3l (const long *v3)
{
vec4l_t v4 = { v3[0], v3[1], v3[2], 0 };
return v4;
}
#ifndef IMPLEMENT_VEC4F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
void
storevec3l (long *v3, vec4l_t v4)
{
v3[0] = v4[0];
v3[1] = v4[1];
v3[2] = v4[2];
}
#endif
#endif//__QF_simd_vec4d_h

View file

@ -33,11 +33,11 @@
#include "QF/simd/types.h"
GNU89INLINE inline vec4f_t vabsf (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vsqrtf (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vceilf (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vfloorf (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vtruncf (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vabs4f (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vsqrt4f (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vceil4f (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vfloor4f (vec4f_t v) __attribute__((const));
GNU89INLINE inline vec4f_t vtrunc4f (vec4f_t v) __attribute__((const));
/** 3D vector cross product.
*
* The w (4th) component can be any value on input, and is guaranteed to be 0
@ -99,6 +99,8 @@ GNU89INLINE inline void storevec3f (float *v3, vec4f_t v4);
GNU89INLINE inline vec4f_t normalf (vec4f_t v) __attribute__((pure));
GNU89INLINE inline vec4f_t magnitudef (vec4f_t v) __attribute__((pure));
GNU89INLINE inline vec4f_t magnitude3f (vec4f_t v) __attribute__((pure));
GNU89INLINE inline vec4i_t loadvec3i (const int *v3) __attribute__((pure));
GNU89INLINE inline void storevec3i (int *v3, vec4i_t v4);
#ifndef IMPLEMENT_VEC4F_Funcs
GNU89INLINE inline
@ -106,7 +108,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4f_t
vabsf (vec4f_t v)
vabs4f (vec4f_t v)
{
const uint32_t nan = ~0u >> 1;
const vec4i_t abs = { nan, nan, nan, nan };
@ -119,7 +121,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4f_t
vsqrtf (vec4f_t v)
vsqrt4f (vec4f_t v)
{
#ifndef __SSE__
vec4f_t r = { sqrtf (v[0]), sqrtf (v[1]), sqrtf (v[2]), sqrtf (v[3]) };
@ -135,7 +137,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4f_t
vceilf (vec4f_t v)
vceil4f (vec4f_t v)
{
#ifndef __SSE4_1__
return (vec4f_t) {
@ -155,7 +157,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4f_t
vfloorf (vec4f_t v)
vfloor4f (vec4f_t v)
{
#ifndef __SSE4_1__
return (vec4f_t) {
@ -175,7 +177,7 @@ GNU89INLINE inline
VISIBLE
#endif
vec4f_t
vtruncf (vec4f_t v)
vtrunc4f (vec4f_t v)
{
#ifndef __SSE4_1__
return (vec4f_t) {
@ -296,11 +298,11 @@ VISIBLE
vec4f_t
qrotf (vec4f_t a, vec4f_t b)
{
vec4f_t ma = vsqrtf (dotf (a, a));
vec4f_t mb = vsqrtf (dotf (b, b));
vec4f_t ma = vsqrt4f (dotf (a, a));
vec4f_t mb = vsqrt4f (dotf (b, b));
vec4f_t den = 2 * ma * mb;
vec4f_t t = mb * a + ma * b;
vec4f_t mba_mab = vsqrtf (dotf (t, t));
vec4f_t mba_mab = vsqrt4f (dotf (t, t));
vec4f_t q = crossf (a, b) / mba_mab;
q[3] = (mba_mab / den)[0];
return q;
@ -388,7 +390,7 @@ VISIBLE
vec4f_t
normalf (vec4f_t v)
{
return v / vsqrtf (dotf (v, v));
return v / vsqrt4f (dotf (v, v));
}
#ifndef IMPLEMENT_VEC4F_Funcs
@ -399,7 +401,7 @@ VISIBLE
vec4f_t
magnitudef (vec4f_t v)
{
return vsqrtf (dotf (v, v));
return vsqrt4f (dotf (v, v));
}
#ifndef IMPLEMENT_VEC4F_Funcs
@ -411,7 +413,7 @@ vec4f_t
magnitude3f (vec4f_t v)
{
v[3] = 0;
return vsqrtf (dotf (v, v));
return vsqrt4f (dotf (v, v));
}
vec4f_t __attribute__((pure))
@ -422,4 +424,29 @@ CircumSphere_vf (const vec4f_t *points, int num_points);
vspheref_t SmallestEnclosingBall_vf (const vec4f_t *points, int num_points);
#ifndef IMPLEMENT_VEC4F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
vec4i_t
loadvec3i (const int *v3)
{
vec4i_t v4 = { v3[0], v3[1], v3[2], 0 };
return v4;
}
#ifndef IMPLEMENT_VEC4F_Funcs
GNU89INLINE inline
#else
VISIBLE
#endif
void
storevec3i (int *v3, vec4i_t v4)
{
v3[0] = v4[0];
v3[1] = v4[1];
v3[2] = v4[2];
}
#endif//__QF_simd_vec4f_h

View file

@ -425,7 +425,7 @@ vector_rem (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
vec4f_t a = *(vec4f_t *) val1->value;
vec4f_t b = *(vec4f_t *) val2->value;
__auto_type c = (vec4f_t *) result->value;
*c = a - b * vtruncf (a / b);
*c = a - b * vtrunc4f (a / b);
}
static void
@ -440,7 +440,7 @@ vector_mod (const exprval_t *val1, const exprval_t *val2, exprval_t *result,
vec4f_t a = *(vec4f_t *) val1->value;
vec4f_t b = *(vec4f_t *) val2->value;
__auto_type c = (vec4f_t *) result->value;
*c = a - b * vfloorf (a / b);
*c = a - b * vfloor4f (a / b);
}
static void

View file

@ -32,11 +32,15 @@
#include <math.h>
#define IMPLEMENT_VEC2F_Funcs
#define IMPLEMENT_VEC2D_Funcs
#define IMPLEMENT_VEC4F_Funcs
#define IMPLEMENT_VEC4D_Funcs
#define IMPLEMENT_MAT4F_Funcs
#include "QF/mathlib.h"
#include "QF/simd/vec2d.h"
#include "QF/simd/vec2f.h"
#include "QF/simd/vec4d.h"
#include "QF/simd/vec4f.h"
#include "QF/simd/mat4f.h"

View file

@ -97,17 +97,17 @@ typedef struct {
#ifdef __AVX2__
static vec4d_t tvtruncd (vec4d_t v, vec4d_t ignore)
{
return vtruncd (v);
return vtrunc4d (v);
}
static vec4d_t tvceild (vec4d_t v, vec4d_t ignore)
{
return vceild (v);
return vceil4d (v);
}
static vec4d_t tvfloord (vec4d_t v, vec4d_t ignore)
{
return vfloord (v);
return vfloor4d (v);
}
static vec4d_t tqconjd (vec4d_t v, vec4d_t ignore)
@ -118,17 +118,17 @@ static vec4d_t tqconjd (vec4d_t v, vec4d_t ignore)
static vec4f_t tvtruncf (vec4f_t v, vec4f_t ignore)
{
return vtruncf (v);
return vtrunc4f (v);
}
static vec4f_t tvceilf (vec4f_t v, vec4f_t ignore)
{
return vceilf (v);
return vceil4f (v);
}
static vec4f_t tvfloorf (vec4f_t v, vec4f_t ignore)
{
return vfloorf (v);
return vfloor4f (v);
}
static vec4f_t tqconjf (vec4f_t v, vec4f_t ignore)
@ -138,12 +138,12 @@ static vec4f_t tqconjf (vec4f_t v, vec4f_t ignore)
static vec4f_t tvabsf (vec4f_t v, vec4f_t ignore)
{
return vabsf (v);
return vabs4f (v);
}
static vec4f_t tvsqrtf (vec4f_t v, vec4f_t ignore)
{
return vsqrtf (v);
return vsqrt4f (v);
}
static vec4f_t tmagnitudef (vec4f_t v, vec4f_t ignore)

View file

@ -156,7 +156,7 @@ calc_plane (vec4f_t v1, vec4f_t v2, int flip, vec4f_t p, vec4f_t *plane)
if (length[0] < ON_EPSILON)
return 0;
*plane /= vsqrtf (length);
*plane /= vsqrt4f (length);
(*plane)[3] = -dotf (p, *plane)[0];
return 1;
}
@ -371,7 +371,7 @@ RecursiveClusterFlow (int clusternum, threaddata_t *thread, pstack_t *prevstack)
// get plane of target_portal, point normal into the neighbor cluster
backplane = -target_portal->plane;
vec4f_t diff = vabsf (pass_plane - backplane);
vec4f_t diff = vabs4f (pass_plane - backplane);
vec4i_t cmp = diff > (vec4f_t) {0.001, 0.001, 0.001, 0.001};
if (!(cmp[0] || cmp[1] || cmp[2])) { // dist isn't interesting
continue; // can't go out a coplanar face