diff --git a/include/twn_draw.h b/include/twn_draw.h index ac60a94..d9af92a 100644 --- a/include/twn_draw.h +++ b/include/twn_draw.h @@ -100,6 +100,20 @@ TWN_API void draw_camera(Vec3 position, /* optional, default: (0, 0, 0) float zoom, /* optional, default: 1 */ float draw_distance); /* optional, default: 100 */ +/* find position and looking direction of a 2d point into 3d space */ +/* make sure it's called with matching parameters to last draw_camera(), if you need them to match */ +typedef struct DrawCameraUnprojectResult { + Vec3 position; + Vec3 direction; +} DrawCameraUnprojectResult; +TWN_API DrawCameraUnprojectResult draw_camera_unproject(Vec2 point, + Vec3 position, + Vec3 direction, + Vec3 up, + float fov, + float zoom, + float draw_distance); + /* same as draw_camera(), but with first person controller in mind */ /* direction and up vectors are inferred from roll, pitch and yaw parameters (in radians) */ /* return value is direction and up vectors, so that you can use them in logic (such as controllers) */ diff --git a/src/rendering/twn_draw.c b/src/rendering/twn_draw.c index 31a6212..a5893bf 100644 --- a/src/rendering/twn_draw.c +++ b/src/rendering/twn_draw.c @@ -5,6 +5,7 @@ #include "twn_types.h" #include "twn_util.h" #include "twn_vec.h" +#include "twn_vec_c.h" #include "twn_deferred_commands.h" #include @@ -497,6 +498,69 @@ DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position, } + +DrawCameraUnprojectResult draw_camera_unproject(Vec2 point, + Vec3 position, + Vec3 direction, + Vec3 up, + float fov, + float zoom, + float draw_distance) +{ + bool const orthographic = fabsf(0.0f - fov) < 0.00001f; + if (!orthographic && fov >= (float)(M_PI)) + log_warn("Invalid fov given (%f)", (double)fov); + + /* inital zoom = 1.0 correlates to perspective from this */ + zoom *= 0.1f; + + float const aspect = (float)ctx.base_render_width / (float)ctx.base_render_height; + Camera const camera = { + .fov = fov, + .pos = position, + .target = vec3_norm(direction), + .up = up, + .viewbox = { + aspect/-zoom, aspect/zoom, + 1/zoom, 1/-zoom + }, + .far_z = draw_distance + }; + + Matrix4 const projection_matrix = orthographic ? camera_orthographic(&camera) : camera_perspective(&camera); + Matrix4 const look_at_matrix = camera_look_at(&camera); + /* TODO: cache matrices for repeated camera inputs, those are expensive */ + Matrix4 const inverse_view_proj = matrix_inverse(matrix_multiply(projection_matrix, look_at_matrix)); + + point.y = (float)ctx.base_render_height - point.y; + + if (!orthographic) { + point = (Vec2) { + point.x, + point.y, + }; + } + + Vec4 v; + v.x = 2.0f * point.x / (float)ctx.base_render_width - 1.0f; + v.y = 2.0f * point.y / (float)ctx.base_render_height - 1.0f; + v.z = 2.0f * 1.0f - 1.0f; + v.w = 1.0f; + + log_vec2(point, "point"); + + v = matrix_vector_multiply(inverse_view_proj, v); + + /* TODO: is it even ever not equal to 1 in our case ? */ + v = vec4_scale(v, 1.0f / v.w); + + return (DrawCameraUnprojectResult){ + .position = (Vec3){ v.x, v.y, v.z }, + .direction = vec3_norm(vec3_sub(position, (Vec3){ v.x, v.y, v.z })), + }; +} + + void set_depth_range(double low, double high) { depth_range_low = low; depth_range_high = high; diff --git a/src/twn_vec_c.h b/src/twn_vec_c.h new file mode 100644 index 0000000..8ca4612 --- /dev/null +++ b/src/twn_vec_c.h @@ -0,0 +1,98 @@ +#ifndef TWN_VEC_C_H +#define TWN_VEC_C_H + +#include "twn_types_c.h" + +#include + + +static inline Vec4 vec4_scale(Vec4 a, float s) { + return (Vec4) { a.x * s, a.y * s, a.z * s, a.w * s }; +} + + +static inline Matrix4 matrix_multiply(Matrix4 a, Matrix4 b) { + Matrix4 dest; + + float const + a00 = a.row[0].x, a01 = a.row[0].y, a02 = a.row[0].z, a03 = a.row[0].w, + a10 = a.row[1].x, a11 = a.row[1].y, a12 = a.row[1].z, a13 = a.row[1].w, + a20 = a.row[2].x, a21 = a.row[2].y, a22 = a.row[2].z, a23 = a.row[2].w, + a30 = a.row[3].x, a31 = a.row[3].y, a32 = a.row[3].z, a33 = a.row[3].w, + + b00 = b.row[0].x, b01 = b.row[0].y, b02 = b.row[0].z, b03 = b.row[0].w, + b10 = b.row[1].x, b11 = b.row[1].y, b12 = b.row[1].z, b13 = b.row[1].w, + b20 = b.row[2].x, b21 = b.row[2].y, b22 = b.row[2].z, b23 = b.row[2].w, + b30 = b.row[3].x, b31 = b.row[3].y, b32 = b.row[3].z, b33 = b.row[3].w; + + dest.row[0].x = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; + dest.row[0].y = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03; + dest.row[0].z = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03; + dest.row[0].w = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03; + dest.row[1].x = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13; + dest.row[1].y = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13; + dest.row[1].z = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13; + dest.row[1].w = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13; + dest.row[2].x = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23; + dest.row[2].y = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23; + dest.row[2].z = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23; + dest.row[2].w = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23; + dest.row[3].x = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; + dest.row[3].y = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; + dest.row[3].z = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; + dest.row[3].w = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; + + return dest; +} + + +static inline Matrix4 matrix_inverse(Matrix4 mat) { + Matrix4 dest; + + float const + a = mat.row[0].x, b = mat.row[0].y, c = mat.row[0].z, d = mat.row[0].w, + e = mat.row[1].x, f = mat.row[1].y, g = mat.row[1].z, h = mat.row[1].w, + i = mat.row[2].x, j = mat.row[2].y, k = mat.row[2].z, l = mat.row[2].w, + m = mat.row[3].x, n = mat.row[3].y, o = mat.row[3].z, p = mat.row[3].w, + + c1 = k * p - l * o, c2 = c * h - d * g, c3 = i * p - l * m, + c4 = a * h - d * e, c5 = j * p - l * n, c6 = b * h - d * f, + c7 = i * n - j * m, c8 = a * f - b * e, c9 = j * o - k * n, + c10 = b * g - c * f, c11 = i * o - k * m, c12 = a * g - c * e, + + idt = 1.0f/(c8*c1+c4*c9+c10*c3+c2*c7-c12*c5-c6*c11), ndt = -idt; + + dest.row[0].x = (f * c1 - g * c5 + h * c9) * idt; + dest.row[0].y = (b * c1 - c * c5 + d * c9) * ndt; + dest.row[0].z = (n * c2 - o * c6 + p * c10) * idt; + dest.row[0].w = (j * c2 - k * c6 + l * c10) * ndt; + + dest.row[1].x = (e * c1 - g * c3 + h * c11) * ndt; + dest.row[1].y = (a * c1 - c * c3 + d * c11) * idt; + dest.row[1].z = (m * c2 - o * c4 + p * c12) * ndt; + dest.row[1].w = (i * c2 - k * c4 + l * c12) * idt; + + dest.row[2].x = (e * c5 - f * c3 + h * c7) * idt; + dest.row[2].y = (a * c5 - b * c3 + d * c7) * ndt; + dest.row[2].z = (m * c6 - n * c4 + p * c8) * idt; + dest.row[2].w = (i * c6 - j * c4 + l * c8) * ndt; + + dest.row[3].x = (e * c9 - f * c11 + g * c7) * ndt; + dest.row[3].y = (a * c9 - b * c11 + c * c7) * idt; + dest.row[3].z = (m * c10 - n * c12 + o * c8) * ndt; + dest.row[3].w = (i * c10 - j * c12 + k * c8) * idt; + + return dest; +} + + +static inline Vec4 matrix_vector_multiply(Matrix4 m, Vec4 v) { + Vec4 res; + res.x = m.row[0].x * v.x + m.row[1].x * v.y + m.row[2].x * v.z + m.row[3].x * v.w; + res.y = m.row[0].y * v.x + m.row[1].y * v.y + m.row[2].y * v.z + m.row[3].y * v.w; + res.z = m.row[0].z * v.x + m.row[1].z * v.y + m.row[2].z * v.z + m.row[3].z * v.w; + res.w = m.row[0].w * v.x + m.row[1].w * v.y + m.row[2].w * v.z + m.row[3].w * v.w; + return res; +} + +#endif